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


    行文目录

      1. 功能效果演示
      1. 实现说明
      • 2.1 其他图片上传
      • 2.2 核心代码:其他图片转Ico
      • 2.3 转换后的Ico文件下载
      1. 总结

    1. 功能效果演示

    仓库地址:IcoTool

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

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

    image

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

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

    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

  • 相关阅读:
    图像轮廓检测
    PostMan接口测试教程
    如何更改文件类型?4个方法,轻松操作!
    JSP第一篇 -----JSP九大内置对象(隐式对象)和四大域对象
    【云原生】基于Kubernetes开发的阿里云ACK之存储管理
    解析java中的多线程的基本概念
    AI语音机器人是否可以设计开放式问题
    RegExp 对象
    Unity SteamVR 开发教程:SteamVR Input 输入系统(2.x 以上版本)
    Laravel artisan 常用命令
  • 原文地址:https://www.cnblogs.com/Dotnet9-com/p/15934168.html