免费开源Blazor在线Ico转换工具
行文目录
- 功能效果演示
- 实现说明
- 2.1 其他图片上传
- 2.2 核心代码:其他图片转Ico
- 2.3 转换后的Ico文件下载
- 总结
1. 功能效果演示
仓库地址:IcoTool
在线演示地址:https://tool.dotnet9.com/ico
演示下文件上传、转换结果:
通过该工具及代码,能了解到:
- 使用Blazor怎么上传文件到服务器(Blazor Server)。
- 怎么从服务器下载文件。
- 如何将png等图片转换为Ico图片。
下面对该工具的实现代码做个简单说明,不太清楚的可以留言交流。
2. 实现说明
通过该工具,能了解到:
- 使用Blazor怎么上传文件到服务器(Blazor Server)。
- 怎么从服务器下载文件。
- 如何将png等图片转换为Ico图片。
下面对该工具的实现代码做个简单说明,不太清楚的可以留言交流。
2.1 其他图片上传
使用的MASA Blazor上传组件MFileInput,看下面的代码,就一个上传组件加上传时文件保存操作,代码文件:IcoTool.razor
<MFileInput TValue="IBrowserFile"
Placeholder="@T("IcoToolMFileInputPlaceholder")"
Rules="_rules"
ShowSize
OnChange="@LoadFile"
Accept="image/png, image/jpeg, image/jpg, image/bmp"
Label="@T("IcoToolMFileInputLabel")">
</MFileInput>
@code {
private bool _loading;
private string _sourceFilePath = "";
[Inject] public I18n I18N { get; set; } = default!;
[Inject] public IJSRuntime Js { get; set; } = default!;
protected override async Task OnInitializedAsync()
{
_rules.Add(value => (value==null|| value.Size < 2 * 1024 * 1024 )? true : T("IcoToolFileSizeLimitMessage"));
await base.OnInitializedAsync();
}
private async Task LoadFile(IBrowserFile? e)
{
if (e == null)
{
_destFilePath = _sourceFilePath = string.Empty;
return;
}
_destFilePath = string.Empty;
if (!string.IsNullOrWhiteSpace(_sourceFilePath) && File.Exists(_sourceFilePath)) File.Delete(_sourceFilePath);
var saveImageDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot", ImageDirName);
if (!Directory.Exists(saveImageDir)) Directory.CreateDirectory(saveImageDir);
_sourceFilePath = Path.Combine(saveImageDir, DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"));
await using var fs = new FileStream(_sourceFilePath, FileMode.Create);
await e.OpenReadStream().CopyToAsync(fs);
}
}
2.2 核心代码:其他图片转Ico
参考代码:https://gist.github.com/darkfall/1656050
因为使用到Bitmap
,vs会提示只支持Windows
平台,目前工具程序也部署在Windows Server 2019
服务器上,如果有其他转换代码,支持跨平台欢迎技术讨论,下面给出我使用的其他图片转Ico的代码,代码路径在:ImagingHelper.cs
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
namespace Dotnet9.Tools.Images;
/// <summary>
/// Adapted from this gist: https://gist.github.com/darkfall/1656050
/// Provides helper methods for imaging
/// </summary>
public static class ImagingHelper
{
public const string FileheadBmp = "6677";
public const string FileheadJpg = "255216";
public const string FileheadPng = "13780";
public const string FileheadGif = "7173";
private static readonly Dictionary<ImageType, string> ImageTypeHead = new()
{
{ ImageType.Bmp, FileheadBmp },
{ ImageType.Jpg, FileheadJpg },
{ ImageType.Png, FileheadPng },
{ ImageType.Gif, FileheadGif }
};
public static bool IsPicture(string filePath, out string fileHead)
{
fileHead = string.Empty;
try
{
var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
var reader = new BinaryReader(fs);
var fileClass = $"{reader.ReadByte().ToString()}{reader.ReadByte().ToString()}";
reader.Close();
fs.Close();
if (fileClass is not (FileheadBmp or FileheadJpg or FileheadPng or FileheadGif))
return false;
fileHead = fileClass;
return true;
}
catch
{
return false;
}
}
public static bool IsPictureType(string filePath, ImageType imageType)
{
var isPicture = IsPicture(filePath, out var fileHead);
if (!isPicture) return false;
return ImageTypeHead[imageType] == fileHead;
}
/// <summary>
/// Converts a PNG image to a icon (ico) with all the sizes windows likes
/// </summary>
/// <param name="inputBitmap">The input bitmap</param>
/// <param name="output">The output stream</param>
/// <returns>Wether or not the icon was succesfully generated</returns>
public static bool ConvertToIcon(Bitmap inputBitmap, Stream output)
{
var sizes = new[] { 256, 48, 32, 16 };
// Generate bitmaps for all the sizes and toss them in streams
var imageStreams = new List<MemoryStream>();
foreach (var size in sizes)
{
var newBitmap = ResizeImage(inputBitmap, size, size);
var memoryStream = new MemoryStream();
newBitmap.Save(memoryStream, ImageFormat.Png);
imageStreams.Add(memoryStream);
}
var iconWriter = new BinaryWriter(output);
var offset = 0;
// 0-1 reserved, 0
iconWriter.Write((byte)0);
iconWriter.Write((byte)0);
// 2-3 image type, 1 = icon, 2 = cursor
iconWriter.Write((short)1);
// 4-5 number of images
iconWriter.Write((short)sizes.Length);
offset += 6 + 16 * sizes.Length;
for (var i = 0; i < sizes.Length; i++)
{
// image entry 1
// 0 image width
iconWriter.Write((byte)sizes[i]);
// 1 image height
iconWriter.Write((byte)sizes[i]);
// 2 number of colors
iconWriter.Write((byte)0);
// 3 reserved
iconWriter.Write((byte)0);
// 4-5 color planes
iconWriter.Write((short)0);
// 6-7 bits per pixel
iconWriter.Write((short)32);
// 8-11 size of image data
iconWriter.Write((int)imageStreams[i].Length);
// 12-15 offset of image data
iconWriter.Write(offset);
offset += (int)imageStreams[i].Length;
}
for (var i = 0; i < sizes.Length; i++)
{
// write image data
// png data must contain the whole png data file
iconWriter.Write(imageStreams[i].ToArray());
imageStreams[i].Close();
}
iconWriter.Flush();
return true;
}
/// <summary>
/// Converts a PNG image to a icon (ico)
/// </summary>
/// <param name="input">The input stream</param>
/// <param name="output">The output stream</param
/// <returns>Wether or not the icon was succesfully generated</returns>
public static bool ConvertToIcon(Stream input, Stream output)
{
var inputBitmap = (Bitmap)Image.FromStream(input);
return ConvertToIcon(inputBitmap, output);
}
/// <summary>
/// Converts a PNG image to a icon (ico)
/// </summary>
/// <param name="inputPath">The input path</param>
/// <param name="outputPath">The output path</param>
/// <returns>Wether or not the icon was succesfully generated</returns>
public static bool ConvertToIcon(string inputPath, string outputPath)
{
using var inputStream = new FileStream(inputPath, FileMode.Open);
using var outputStream = new FileStream(outputPath, FileMode.OpenOrCreate);
return ConvertToIcon(inputStream, outputStream);
}
/// <summary>
/// Converts an image to a icon (ico)
/// </summary>
/// <param name="inputImage">The input image</param>
/// <param name="outputPath">The output path</param>
/// <returns>Wether or not the icon was succesfully generated</returns>
public static bool ConvertToIcon(Image inputImage, string outputPath)
{
using var outputStream = new FileStream(outputPath, FileMode.OpenOrCreate);
return ConvertToIcon(new Bitmap(inputImage), outputStream);
}
/// <summary>
/// Resize the image to the specified width and height.
/// Found on stackoverflow: https://stackoverflow.com/questions/1922040/resize-an-image-c-sharp
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
public static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using var graphics = Graphics.FromImage(destImage);
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using var wrapMode = new ImageAttributes();
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
return destImage;
}
}
public enum ImageType
{
Bmp,
Jpg,
Png,
Gif
}
简单的单元测试还是要有的,代码见:ImageHelperTests.cs
using Dotnet9.Tools.Images;
namespace Dotnet9.Tools.Tests.Images;
public class ImageHelperTests
{
[Fact]
public void IsPicture()
{
var testFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestFiles", "logo.png");
Assert.True(File.Exists(testFilePath));
var isPicture = ImagingHelper.IsPicture(testFilePath, out var typename);
Assert.True(isPicture);
}
[Fact]
public void IsNotPicture()
{
var testFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestFiles", "test.txt");
Assert.True(File.Exists(testFilePath));
var isPicture = ImagingHelper.IsPicture(testFilePath, out var typename);
Assert.False(isPicture);
}
[Fact]
public void IsPngFile()
{
var testFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestFiles", "logo.png");
Assert.True(File.Exists(testFilePath));
var isPng = ImagingHelper.IsPictureType(testFilePath, ImageType.Png);
Assert.True(isPng);
}
[Fact]
public void ShouldConvertPngToIcon()
{
var sourcePng = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestFiles", "logo.png");
var destIco = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestFiles", "logo.ico");
Assert.True(File.Exists(sourcePng));
Assert.False(File.Exists(destIco));
ImagingHelper.ConvertToIcon(sourcePng, destIco);
Assert.True(File.Exists(destIco));
File.Delete(destIco);
}
}
页面调用Ico转换功能代码如下,提供一个触发转换的按钮和执行转换的方法,代码文件:IcoTool.razor
@if (!string.IsNullOrWhiteSpace(_sourceFilePath) && File.Exists(_sourceFilePath))
{
<MButton class="ma-2 white--text"
Loading="_loading"
Disabled="_loading"
Depressed Color="primary"
OnClick="@ConvertToIcon">
<LoaderContent>
<span>@T("IcoToolMButtonLoaderContent")</span>
</LoaderContent>
<ChildContent>
<span>@T("IcoToolMButtonChildContent")</span>
</ChildContent>
</MButton>
}
@code {
private async Task ConvertToIcon()
{
if (!string.IsNullOrWhiteSpace(_destFilePath) && File.Exists(_destFilePath))
{
await DownloadIco();
return;
}
_loading = true;
if (!string.IsNullOrWhiteSpace(_sourceFilePath) && File.Exists(_sourceFilePath))
{
_destFilePath = $"{_sourceFilePath}.ico";
if (ImagingHelper.ConvertToIcon(_sourceFilePath, _destFilePath)) await DownloadIco();
}
_loading = false;
}
}
2.3 转换后的Ico文件下载
文件转换成功后,怎么提供下载呢?
起初想使用一个<a href="/files/xxx.ico" target="_blank">xxx.ico</a>
标签提供浏览下载的,但动态生成的图片无法访问,不知道什么原因,只能暂时采用一个折衷的方式,有朋友有好的想法欢迎留言。
目前采用的是提供按钮下载,下面是封装的js下载方法,来自微软的文档:ASP.NET Core Blazor file downloads
我把JS
代码放_Layout.cshtml:
<script>
// 省略部分代码
async function downloadFileFromStream(fileName, contentStreamReference) {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
triggerFileDownload(fileName, url);
URL.revokeObjectURL(url);
}
function triggerFileDownload(fileName, url) {
const anchorElement = document.createElement('a');
anchorElement.href = url;
if (fileName) {
anchorElement.download = fileName;
}
anchorElement.click();
anchorElement.remove();
}
</script>
页面下载时使用以下代码,使用到JS互操作
(什么是JS互操作
?可以参考我转载的这篇文章了解首页.NETBlazorBlazor Server
(14/30)大家一起学Blazor:JavaScript interop(互操作)),代码放:IcoTool.razor
@inject IJSRuntime JS
// 省略n多代码
@code {
private async Task DownloadIco()
{
await using var fileStream = new FileStream(_destFilePath, FileMode.Open);
using var streamRef = new DotNetStreamReference(fileStream);
await Js.InvokeVoidAsync("downloadFileFromStream", Path.GetFileName(_destFilePath), streamRef);
}
}
3. 总结
- Blazor组件库使用的MASA Blazor,很美观大方的
Material Design
设计风格。 - Ico转换,使用到了
System.Drawing.Common
包的Bitmap
,.NET 6开始不支持跨平台,提示只支持Windows
平台。 - 本工具使用7.0.100-preview.1开发、编译、上线,使用.NET 6的同学,请放心使用,可以无缝升级。
Dotnet9工具箱
会不断添加新的免费、开源、在线工具,欢迎star支持,有什么需求我会考虑加上,仓库地址:Dotnet9.Tools,可提交issue、网站留言、微信公众号(dotnet9)联系等等。
本工具源码:IcoTool
介绍文章:Blazor在线Ico转换工具
在线演示地址:https://tool.dotnet9.com/ico
免费开源Blazor在线Ico转换工具的更多相关文章
- web字体格式及几种在线格式转换工具介绍
原文地址:http://blog.csdn.net/xiaolongtotop/article/details/8316554 目前,文字信息仍是网站最主要的内容,随着CSS3技术的不断成熟,Web字 ...
- yml在线格式转换工具(properties)
分享一个在线properties 转 yml工具,也支持yml转properties, 域名非常简单好记,直接在地址栏里输入toyaml.com,地址:http://toyaml.com/ yml,即 ...
- 推荐10款免费的在线UI测试工具
发布网站之前至关重要的一步是网站测试.网站测试要求我们全面地运行网站并通过所有基本测试,如响应式设计测试.安全测试.易用性测试.跨浏览器兼容性.网站速度测试等. 网站测试对SEO.搜索引擎排名.转换率 ...
- GitHub 托管的10款免费开源 windows 工具
GitHub 是如今所有开源事物的中央仓库, 这个网站最近发布了一个叫做<2016 Octoverse 状态报告>,详细列出了从去年起其一系列亮点, 包括总的活跃用户数,最常见的 emo ...
- C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志
C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...
- GitHub 上 10 款免费开源 Windows 工具
GitHub 上 10 款免费开源 Windows 工具 GitHub 是如今所有开源事物的中央仓库, 这个网站最近发布了一个叫做<2016 Octoverse 状态报告>,详细列出了从 ...
- 开源项目在线化 中文繁简体转换/敏感词/拼音/分词/汉字相似度/markdown 目录
前言 以前在 github 上自己开源了一些项目.碍于技术与精力,大部分项目都是 java 实现的. 这对于非 java 开发者而言很不友好,对于不会编程的用户更加不友好. 为了让更多的人可以使用到这 ...
- Unicode编码解码在线转换工具
// Unicode编码解码在线转换工具 Unicode 是基于通用字符集(Universal Character Set)的标准来发展,并且同时也以书本的形式(The Unicode Standar ...
- 严重推荐一个免费开源数据库建模工具软件 --OpenSystemArchitect 4.0
嘿嘿,对于我这样的新手,这个工具还是很令人兴奋的. 真的是术业有专攻啊.关键还是免费开源 EXCEL,VISO,PPT,PS,CD,FREEHAND不是不可以,只是.人家还是专业点,方便点.. Ope ...
随机推荐
- JAVA8-STREAM 使用说明
概述 本人在java开发过程中,有些知识点需要记录整理,我尽量严谨的叙述我学习的经过和心得,以便备份和和大家一起进步学习,此篇文章是在网上多出搜集整理验证,结尾会注明出处,今天学习一个java8新的功 ...
- 彻彻底底地理解TCP三次握手和四次挥手的全部过程
三次握手 我们先提出一些问题,但是我们暂且不回答这些问题,下面我会尽我所能详尽地讲解TCP的三次握手过程,然后看完你可以在评论区留下你对问题的答案,我们可以一起探讨. 为什么要握手 为什么是三次而不是 ...
- go包管理速通,一篇文章就够了,再也不用担心因为不会导包被辞退
前言 最近在看一些go语言相关的书,发现了一个有意思的事情:其中一本书最新印刷的版本是2017年3月,而golang包管理的后起之秀go module伴随go1.11于2018年8月诞生--因此,书里 ...
- access注入 - 联合查询
1.access数据库简介 简介:Microsoft Office Access是由微软发布的关系数据库管理系统.它结合了 MicrosoftJet Database Engine 和 图形用户界面两 ...
- RHCSA 第八天
1.查询ip的几种方式: ip, ifconfig, nmcli,nmtui 2.nmcli命令使用: a.在ens160网卡上新建连接static_con,并配置静态ip b.在ens160网卡上新 ...
- VirtualBox 安装 Ubuntu 20.04 全流程
VirtualBox 安装 Ubuntu 20.04 全流程 内容概要 这个作业属于哪个课程 2022面向对象程序设计 这个作业要求在哪里 2022面向对象程序设计寒假作业1 这个作业的目标 在虚拟机 ...
- 达索CATIA许可证(License)管理使用和优化
现下主流的V6版本CATIA,是由达索公司提供授权的浮动型License,其客户端通过企业内网从许可证服务器获得许可证,最少要有一个服务器端DS License Server提供一定数量的Licens ...
- 什么是以特性为核心的持续交付|阿里巴巴DevOps实践指南
编者按:本文源自阿里云云效团队出品的<阿里巴巴DevOps实践指南>,扫描上方二维码或前往:https://developer.aliyun.com/topic/devops,下载完整版电 ...
- Nginx限制连接控制访问量
目录 一:限制连接数模块(同时访问网址能访问多少次) 1.修改网址模块文件 2.测试 3.重启 4.增加解析ip 5.压力测试 二:控制Nginx访问量 1.连接池 2.限制数 3.测试 4.重启 5 ...
- VUE3 之 插槽的使用 - 这个系列的教程通俗易懂,适合新手
1. 概述 非理性定律告诉我们: 人们总是习惯于以情感去判断眼前的事物,非理性的去做决定. 对于长远的利益,人们更愿意去选择短期的利益. 因此在做决定前要让自己冷静,理性的分析,让自己看的更远. 言归 ...