1. Function effect demonstration
Warehouse address: IcoTool
Online presentation address: https://tool.dotnet9.com/ico
Demonstrate the file upload and conversion results:
Through this tool and code, we can understand:
- How to upload files to the server (Blazor Server) using Blazor.
- How to download files from the server.
- How to convert pictures such as png into Ico pictures.
The following is a brief description of the implementation code of the tool. If you are not clear, you can leave a message for communication.
2. Implementation description
Through this tool, we can understand:
- How to upload files to the server (Blazor Server) using Blazor.
- How to download files from the server.
- How to convert pictures such as png into Ico pictures.
The following is a brief description of the implementation code of the tool. If you are not clear, you can leave a message for communication.
2.1 upload of other pictures
Used MASA Blazor Upload component MFileInput , look at the following code to add the file saving operation when uploading to an upload component, code file: 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 core code: other pictures transferred to Ico
Reference code: https://gist.github.com/darkfall/1656050
Because Bitmap is used, vs will prompt that it only supports windows platform. At present, the tool program is also deployed on Windows Server 2019 server. If there are other conversion codes, it supports cross platform. Welcome to the technical discussion. Here are the codes for converting other pictures I use to Ico. The code path is: 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 }
Simple unit tests are still necessary. See the code below: 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); } }
The code of calling Ico conversion function on the page is as follows, providing a button to trigger the conversion and a method to execute the conversion. The code file is as follows: 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 download of converted Ico files
After the file conversion is successful, how can I download it?
Initially, I wanted to use a < a href = "/ files / xxx. ICO" target = "_blank" > XXX ICO < / a > tag provides browsing and downloading, but the dynamically generated pictures cannot be accessed. I don't know why, so I can only temporarily adopt a compromise way. If friends have good ideas, please leave a message.
At present, the button download method is adopted. The following is the encapsulated js download method, which comes from Microsoft documents: ASP.NET Core Blazor file downloads
I put the JS code _Layout.cshtml:
<script> // Omit some codes 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>
When downloading the page, use the following code to use js interoperability (what is JS interoperability? You can refer to this article I reprint for details home page NETBlazorBlazor Server
(14 / 30) let's learn Blazor: Javascript interop ), code: IcoTool.razor
@inject IJSRuntime JS // Omit n multiple codes @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. Summary
- Used by Blazor component library MASA Blazor , very beautiful and generous Material Design style.
- Ico conversion, using system Drawing. Bitmap of common package NET 6 does not support cross platform, and the prompt only supports Windows platform.
- Use of this tool 7.0.100-preview.1 Develop, compile, launch and use .NET 6 Students, please rest assured that you can upgrade seamlessly.
Dotnet9 toolbox will continue to add new free, open source and online tools. Welcome star support. If there is any demand, I will consider adding it. Warehouse address: Dotnet9.Tools , but Submit issue,Website message WeChat official account (dotnet9) contact and so on.
Source code of this tool: IcoTool
Introduction article: Blazor online Ico conversion tool
Online presentation address: https://tool.dotnet9.com/ico