免费开源Blazor在线Ico转换工具

1. 功能效果演示

仓库地址:IcoTool[1]

在线演示地址:https://tool.dotnet9.com/ico[2]

演示下文件上传、转换结果:

2b26597caeeb4d126ce2e80f3c57b58a.gif

通过该工具及代码,能了解到:

  1. 使用Blazor怎么上传文件到服务器(Blazor Server)。

  2. 怎么从服务器下载文件。

  3. 如何将png等图片转换为Ico图片。

下面对该工具的实现代码做个简单说明,不太清楚的可以留言交流。

2. 实现说明

通过该工具,能了解到:

  1. 使用Blazor怎么上传文件到服务器(Blazor Server)。

  2. 怎么从服务器下载文件。

  3. 如何将png等图片转换为Ico图片。

下面对该工具的实现代码做个简单说明,不太清楚的可以留言交流。

2.1 其他图片上传

使用的MASA Blazor[3]上传组件MFileInput[4],看下面的代码,就一个上传组件加上传时文件保存操作,代码文件:IcoTool.razor[5]

<MFileInput TValue="IBrowserFile"Placeholder="@T("IcoToolMFileInputPlaceholder")"Rules="_rules"ShowSizeOnChange="@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[6]

因为使用到Bitmap,vs会提示只支持Windows平台,目前工具程序也部署在Windows Server 2019服务器上,如果有其他转换代码,支持跨平台欢迎技术讨论,下面给出我使用的其他图片转Ico的代码,代码路径在:ImagingHelper.cs[7]

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 streamsvar 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, 0iconWriter.Write((byte)0);iconWriter.Write((byte)0);// 2-3 image type, 1 = icon, 2 = cursoriconWriter.Write((short)1);// 4-5 number of imagesiconWriter.Write((short)sizes.Length);offset += 6 + 16 * sizes.Length;for (var i = 0; i < sizes.Length; i++){// image entry 1// 0 image widthiconWriter.Write((byte)sizes[i]);// 1 image heighticonWriter.Write((byte)sizes[i]);// 2 number of colorsiconWriter.Write((byte)0);// 3 reservediconWriter.Write((byte)0);// 4-5 color planesiconWriter.Write((short)0);// 6-7 bits per pixeliconWriter.Write((short)32);// 8-11 size of image dataiconWriter.Write((int)imageStreams[i].Length);// 12-15 offset of image dataiconWriter.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 fileiconWriter.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[8]

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[9]

@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[10]

我把JS代码放_Layout.cshtml[11]

<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[12]

@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. 总结

  1. Blazor组件库使用的MASA Blazor[13],很美观大方的Material Design设计风格。

  2. Ico转换,使用到了System.Drawing.Common包的Bitmap,.NET 6开始不支持跨平台,提示只支持Windows平台。

  3. 本工具使用7.0.100-preview.1[14]开发、编译、上线,使用.NET 6[15]的同学,请放心使用,可以无缝升级。

Dotnet9工具箱会不断添加新的免费、开源、在线工具,欢迎star支持,有什么需求我会考虑加上,仓库地址:Dotnet9.Tools[16],可提交issue[17]、网站留言[18]、微信公众号(dotnet9)联系等等。

本工具源码:IcoTool[19]

介绍文章:Blazor在线Ico转换工具[20]

在线演示地址:https://tool.dotnet9.com/ico[21]

参考资料

[1]

IcoTool: https://github.com/dotnet9/dotnet9.com/blob/develop/src/Dotnet9.Tools.Web/Pages/Public/ImageTools/IcoTool.razor

[2]

https://tool.dotnet9.com/ico: https://tool.dotnet9.com/ico

[3]

MASA Blazor: https://masa-blazor-docs-dev.lonsid.cn/

[4]

MFileInput: https://masa-blazor-docs-dev.lonsid.cn/components/file-inputs

[5]

IcoTool.razor: https://github.com/dotnet9/dotnet9.com/blob/develop/src/Dotnet9.Tools.Web/Pages/Public/ImageTools/IcoTool.razor

[6]

https://gist.github.com/darkfall/1656050: https://gist.github.com/darkfall/1656050

[7]

ImagingHelper.cs: https://github.com/dotnet9/dotnet9.com/blob/develop/src/Dotnet9.Tools/Images/ImagingHelper.cs

[8]

ImageHelperTests.cs: https://github.com/dotnet9/dotnet9.com/blob/develop/src/test/Dotnet9.Tools.Tests/Images/ImageHelperTests.cs

[9]

IcoTool.razor: https://github.com/dotnet9/dotnet9.com/blob/develop/src/Dotnet9.Tools.Web/Pages/Public/ImageTools/IcoTool.razor

[10]

ASP.NET Core Blazor file downloads: https://docs.microsoft.com/en-us/aspnet/core/blazor/file-downloads?view=aspnetcore-6.0

[11]

_Layout.cshtml: https://github.com/dotnet9/dotnet9.com/blob/develop/src/Dotnet9.Tools.Web/Pages/_Layout.cshtml

[12]

IcoTool.razor: https://github.com/dotnet9/dotnet9.com/blob/develop/src/Dotnet9.Tools.Web/Pages/Public/ImageTools/IcoTool.razor

[13]

MASA Blazor: https://masa-blazor-docs-dev.lonsid.cn/

[14]

7.0.100-preview.1: https://dotnet.microsoft.com/en-us/download/dotnet/7.0

[15]

.NET 6: https://dotnet.microsoft.com/en-us/download/dotnet/6.0

[16]

Dotnet9.Tools: https://github.com/dotnet9/dotnet9.com

[17]

提交issue: https://github.com/dotnet9/dotnet9.com/issues/new

[18]

网站留言: https://dotnet9.com

[19]

IcoTool: https://github.com/dotnet9/dotnet9.com/blob/develop/src/Dotnet9.Tools.Web/Pages/Public/ImageTools/IcoTool.razor

[20]

Blazor在线Ico转换工具: https://dotnet9.com/?p=1715(在新窗口中打开)

[21]

https://tool.dotnet9.com/ico: https://tool.dotnet9.com/ico

e425edebdbeee532af713cdf5206d9fa.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/291054.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【02】CC - 有意义的命名

为什么80%的码农都做不了架构师&#xff1f;>>> 1、提防使用不同之处较小的名称 XYZControllerForEfficientHandlingOfStrings 与 XYZControllerForEfficientStorageOfStrings 在IDE下&#xff0c;都有自动补全&#xff0c;这种细微的差别&#xff0c;容易补全错&a…

linux7 语言包,Centos 7中文语言包的安装及中文支持

1、修改配置文件etc/locale.confLANG"zh_CN.UTF-8"2、查看更改后的系统语言变量[root5c46832b5c01 ~]# localelocale: Cannot set LC_CTYPE to default locale: No such file or directorylocale: Cannot set LC_MESSAGES to default locale: No such file or direct…

如何在构建docker镜像时执行SonarQube扫描.NET Core应用

前言SonarQube是一款静态代码质量分析工具&#xff0c;它常用于检测代码中的Bug、漏洞和代码异味&#xff0c;并且能够集成在IDE、Jenkins、Git等服务中&#xff0c;方便随时查看代码质量分析报告。一般情况下&#xff0c;我们在Jenkins管道中配置SonarQube&#xff0c;在编译过…

Win10系列:VC++ Direct3D模板介绍1

Visual Studio为开发Direct3D应用程序提供了便捷的模版&#xff0c;读者可以不必手动去新建Direct3D中所使用到的基础资源&#xff0c;而只需专注于图形的绘制。本小节主要为读者介绍这个模版中用于绘制图形的主要函数及其功能&#xff0c;为了能让读者更为清楚地了解如何使用此…

linux+arch系统下载,Linux

大小&#xff1a; 695MB更新时间&#xff1a;2021-02-04适用电脑&#xff1a;系统盘大于20G超过1GMHz的处理器最佳64位处理器Arch Linux是一份独立开发的、为i686优化的Linux发行&#xff0c;它面向高级Linux用户。它使用自行开发的包管理器pacman来为最新的应用软件提供 更新升…

Android---AlarmManager(全局定时器/闹钟)指定时长或以周期形式执行某项操作

AlarmManager的使用机制有的称呼为全局定时器&#xff0c;有的称呼为闹钟。通过对它的使用&#xff0c;个人觉得叫全局定时器比较合适&#xff0c;其实它的作用和Timer有点相似。都有两种相似的用法&#xff1a;&#xff08;1&#xff09;在指定时长后执行某项操作&#xff08;…

C# 实例解释面向对象编程中的里氏替换原则

在面向对象编程中&#xff0c;SOLID 是五个设计原则的首字母缩写&#xff0c;旨在使软件设计更易于理解、灵活和可维护。这些原则是由美国软件工程师和讲师罗伯特C马丁(Robert Cecil Martin)提出的许多原则的子集&#xff0c;在他2000年的论文《设计原则与设计模式》中首次提出…

Android之JNI ERROR (app bug): accessed stale global reference 0xb39533f2 (index 19708 in a table of s

1、问题 2、原因 我在jni里面是这样写的 (*env)->CallVoidMethod(env, obj, method3, "chenyu"); 3、解决办法 把这个 (*env)->CallVoidMethod(env, obj, method3, "chenyu"); 改为这个 (*env)->CallVoidMethod(env, obj, method3, (*env)-&g…

工业互联网的最后一公里

最后一公里&#xff0c;出自中国共产党十八大以来的新名词之一&#xff0c;指政策始终“走在路上”&#xff0c;服务始终“停在嘴上”&#xff0c;实惠没有真正“落在身上”的“末梢堵塞”问题。要让人民群众真正得实惠&#xff0c;就要切实解决好“最后一公里”问题。1、移动互…

介绍这个库:C# Blazor中显示Markdown文件

1 讲目的 前几天上线了一个在线Icon转换工具[1]&#xff0c;为了让大家使用放心&#xff0c;改了点代码&#xff0c;在转换下载Icon图标后立即删除临时文件&#xff0c;并在工具下面贴上了工具的开发步骤和代码&#xff0c;大家看这样改是否合适&#xff0c;见Issue 1[2]。这篇…

Linux 信号量 生产者消费者小例题

菜鸟偶遇信号量&#xff0c;擦出火花&#xff08;只有不熟才会有火花&#xff09;。于是上网搜资料和看《Unix环境高级编程》实现了几个小例题&#xff0c;高手请勿喷&#xff01;这几位写得非常好啊&#xff1a; 题目来源&#xff1a; http://www.it165.net/os/html/201312/70…

C/C++语言之通过定义指针函数方式来实现在一个cpp文件里面获取另外一个cpp文件函数的返回值

1、定义函数指针 typedef int (* fun) (); static fun f; 2、代码实现 3、结果 4、总结 我们可以这样使用 在a.h文件里面里面定义函数指针,并且有个传递函数指针的方法 typedef std::string (*fun)();void f2(fun f 1); 然后在a.cpp文件里面实现f2方法 static fun f;…

Dapr 中文社区汇总

Dapr 于 2019 年在微软创建。随着时间的推移&#xff0c;许多社区成员加入该项目并做出贡献&#xff0c;扩展并帮助它在 2021 年 2 月达到了稳定的 1.0 版本。2021年3 月提交给 CNCF&#xff0c;在2021年11月被接受 Dapr 作为 CNCF 的孵化项目。关于CNCF 的每个级别的成熟度要求…

逻辑查询优化

为什么80%的码农都做不了架构师&#xff1f;>>> 1、逻辑查询优化 基于关系代数理论&#xff0c;启发式规则&#xff0c;对查询进行等价重写。 2、查询重写规则 &#xff08;1&#xff09;子查询优化 &#xff08;2&#xff09;视图重写 &#xff08;3&#xff09;等…

CentOS 7 Root用户密码重置 2017-04-02

跨平台系列汇总&#xff1a;http://www.cnblogs.com/dunitian/p/4822808.html#linux 异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 开机的时候按e &#xff08;如果正在使用&#xff0c;你可以输入reboot&#xff0c;然后赶紧按回车键&#xff0c;也…

oracle处理考勤时间,拆分考勤时间段的sql语句

最近一直在用mysql数据库做云项目,有段时间没有接触oracle了,昨天有朋友叫我帮忙用oracle处理一个考勤记录的需求,我在考虑如何尽量精简实现上面花了一些时间。于是把这个实现做个总结。 需求如下: rownum为奇数的为进厂时间&#xff0c;偶数的为离场时间第一个奇数行的时间被第…

实现DDD领域驱动设计: Part 2

原文链接: https://dev.to/salah856/implementing-domain-driven-design-part-ii-2i36实现&#xff1a;构建块这是本系列的重要部分。我们将通过示例介绍和解释一些明确的规则。在实现领域驱动设计时&#xff0c;你可以遵循这些规则并应用到你的解决方案中。示例示例将使用GitH…

王彪20162321 2016-2017-2 《程序设计与数据结构》第5周学习总结

王彪 2016-2017-2 《程序设计与数据结构》第5周学习总结 教材学习内容总结 1.关键概念 1.面向对象程序设计的核心是类的定义&#xff0c;它代表了状态和行为的对象。2.变量的作用域依赖于变量声明的位置&#xff0c;作用域决定在哪里可以使用变量。3.对象应该是封装的&#xff…

c语言指针索引数组,C语言数组指针表示法

指针在处理数组时很有用&#xff0c;我们可以用指针指向已有的数组&#xff0c;也可以从堆上分配内存然后把这块内存当做一个数组使用。数组表示法和指针表示法在某种意义上可以互换。不过&#xff0c;它们并不完全相同&#xff0c;后面的“数组和指针的差别”中会详细说明。单…

C# 使用AggregateException 信息

为了得到所有失败任务的异常信息&#xff0c;可以将 Task.WhenAll 返回的结果写到一个Task 变量中。这个任务会一直等到所有任务都结束。否则&#xff0c;仍然可能错过抛出的异常。上一小节中&#xff0c;catch 语句只检索到第一个任务的异常。不过&#xff0c;现在可以访问外部…