1. 功能效果演示

仓库地址:IcoTool

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

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

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

  1. 使用Blazor怎么上传文件到服务器(Blazor Server)。
  2. 怎么从服务器下载文件。
  3. 如何将png等图片转换为Ico图片。

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

2. 实现说明

通过该工具,能了解到:

  1. 使用Blazor怎么上传文件到服务器(Blazor Server)。
  2. 怎么从服务器下载文件。
  3. 如何将png等图片转换为Ico图片。

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

2.1 其他图片上传

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

<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

因为使用到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 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

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

  1. Blazor组件库使用的MASA Blazor,很美观大方的Material Design设计风格。
  2. Ico转换,使用到了System.Drawing.Common包的Bitmap,.NET 6开始不支持跨平台,提示只支持Windows平台。
  3. 本工具使用7.0.100-preview.1开发、编译、上线,使用.NET 6的同学,请放心使用,可以无缝升级。

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

本工具源码:IcoTool

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

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

免费开源Blazor在线Ico转换工具相关推荐

  1. 28个免费在线格式转换工具

    注:本文非原创,点击查看原帖 多种文件格式互相转换的在线工具越来越多,这里推荐如下: 1) Zamzar.com – 在线文件转换中的一个快速而便捷的方式.不需要注册,转换完的文件回到您的电子邮件里面 ...

  2. 强大免费的在线格式转换工具,三步轻松完成。

    大家平时工作中会遇到各种不同类型的文件,文件格式转换是经常会进行的操作,但格式转换软件下载太麻烦,还有可能会存在恶意捆绑软件的现象,安全性不高,今天和大家分享一个在线格式转换工具,电脑网页和手机端都可 ...

  3. 最好的在线PDF转换工具服务

    工作中有时候会碰到需要转换PDF文件的情况,现在网上就要很多免费的在线工具,可以进行PDF文件的转换,下面就来介绍一些可以直接在浏览器中将文档.电子表格.和图片转换为PDF或者互相转换的服务工具. 原 ...

  4. 这 6 款在线 PDF 转换工具,得试试

    读者提问: 免费好用的在线 PDF 转换工具有推荐的吗 ? 阿常回答: 有,这 6 款在线 PDF 转换工具,免费实用,快来试试吧! 1.pdftoword(支持 PDF 与 Word.TXT.图片. ...

  5. 分享12款优秀的在线文件格式转换工具

    在线的文件转换工具有很多,但是要找到一款好用的却不容易,所以今天这篇文章收集了12款优秀的在线文件格式转换工具,看看有没有适合您的需要的工具. iWebPrint 打印网页为PDF文件,可以定制打印尺 ...

  6. 10款方便的在线文件格式转换工具网站。

    1.online-convert online-convert是一个十分优秀的在线格式转换应用,支持格式覆盖文档.图片.音频.视频.电子书.Flash以及一些 不常见的格式.使用非常简单,你只需根据在 ...

  7. 这 6 款在线 PDF 转换工具,得试

    读者提问: 免费好用的在线 PDF 转换工具有推荐的吗 ? 阿常回答: 有,这 6 款在线 PDF 转换工具,免费实用,快来试试吧! 1.pdftoword(支持 PDF 与 Word.TXT.图片. ...

  8. 10款优秀的在线格式转换工具

    1.online-convert online-convert是一个十分优秀的在线格式转换应用,支持格式覆盖文档.图片.音频.视频.电子书.Flash以及一些 不常见的格式.使用非常简单,你只需根据在 ...

  9. 获取当前时间戳-在线时间戳转换工具

    常用语言获取当前时间戳 Swift NSDate().timeIntervalSince1970 Go import ("time")int32(time.Now().Unix() ...

  10. 在线摩斯密码在线翻译转换工具

    在线摩斯密码在线翻译转换工具 在线摩斯密码在线翻译转换工具 本工具可以在浏览器本地将文本转换成摩斯密码,也可以将摩斯密码翻译成可视化文本内容. 本工具可以在浏览器本地将文本转换成摩斯密码,也可以将摩斯 ...

最新文章

  1. 【微信小程序】登录功能实现及讲解(获取用户唯一标识)
  2. 从零开始一起学习SLAM | 点云到网格的进化
  3. python 中类属性共享问题
  4. 关于MySQL 主从复制问题
  5. 1、【设计模式】组合模式
  6. Redis的安装与部署
  7. 手机使用python操作图片文件
  8. 火爆GitHub!3.2k Star的可视化神器开源!
  9. ictclas4j java_java使用ictclas4j分词时出现NullPointerException错误 寻高手帮忙
  10. ONNX系列四 --- 使用ONNX使TensorFlow模型可移植
  11. 表达式求值(NOIP2013 普及组第二题)
  12. jQuery的Select操作集合
  13. Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds
  14. Flutter之Dialog使用和踩坑
  15. 仓库建设-斜率优化DP
  16. (Keras/监督学习)15分钟搞定最新深度学习车牌OCR
  17. Andriod 对号错号
  18. LS-DYNA基础理论
  19. Android Studio中ListView通过自定义Adapter显示数据3-1
  20. AssertionError: CUDA unavailable, invalid device 0 requested

热门文章

  1. 计算机发展英文文献,计算机技术发展英文参考文献 计算机技术发展论文参考文献哪里找...
  2. Windows 启动项被误删除,EFI分区误格式化恢复
  3. c语言程序设计九宫格,C语言课程设计之智力九宫格.doc
  4. 数美科技:全栈防御体系怎么样护航游戏ROI增长
  5. 电脑计算机怎么显示到桌面,怎么显示我的电脑到桌面
  6. vue 定制上传按钮的样式的两种方法
  7. 【图像超分辨率】Deep Learning for Image Super-resolution: A Survey
  8. 网站目标定位的关键词和选择质量高的关键词
  9. formidable词根词缀_SAT词根词缀汇总内容(6)
  10. 登录验证时第一次帐号密码错误,第二次提交出现错误404,Could not find action or result: /zyf_shop/user_login.action