The free open source Blazor online Ico conversion tool does not save source files and converted files, and deletes them as soon as you download them. Please use them with confidence.
** Text catalog **
-
- function demonstration
-
- Implementation description
- 2.1 Upload other pictures
- 2.2 Core code: Other pictures to Ico
- 2.3 Download of converted Ico file
-
- summary
1. function demonstration
仓库地址:IcoTool
在线演示地址:https://tool.dotnet9.com/ico
Demonstrate the file upload and conversion results:

Through this tool and code, you can learn:
- How to upload files to the server using Blazor (Blazor Server).
- How to download files from the server.
- How to convert png and other pictures into Ico pictures.
The following is a brief explanation of the implementation code of the tool. If you are not clear, you can leave a message and communicate.
2. Implementation description
2.1 Upload other pictures
使用的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 readonly List<Func<IBrowserFile, StringBoolean>> _rules = new();
private bool _loading;
private IBrowserFile? _sourceBrowserFile;
[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 void LoadFile(IBrowserFile? e)
{
_sourceBrowserFile = e;
}
}
上面的代码,看LoadFile(IBrowserFile? e)方法,选择文件时只保存IBrowserFile引用,选择了后即显示立即转换并下载Icon按钮,代码如下:
@if (_sourceBrowserFile != null) {
<MButton
class="ma-2 white--text"
Loading="_loading"
Disabled="_loading"
Depressed
Color="primary"
OnClick="@ConvertAndDownloadIcon"
>
<LoaderContent>
<span>@T("IcoToolMButtonLoaderContent")</span>
</LoaderContent>
<ChildContent>
<span>@T("IcoToolMButtonChildContent")</span>
</ChildContent>
</MButton>
}
此时,源文件并未做上传操作,用户还可以重新选择,点击立即转换并下载Icon按钮才会执行图标转换和下载操作,下面会说。
2.2 Core code: Other pictures to Ico
参考代码:https://gist.github.com/darkfall/1656050
因为使用到Bitmap,vs 会提示只支持Windows平台(注意重点:只是该包System.Drawing.Common不支持跨平台,不是.NET 6 不支持跨平台,.NET 6 支持跨平台。),目前工具程序也部署在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);
}
}
用户确认选择好文件后,点击前面提过的按钮立即转换并下载Icon执行转换的方法ConvertAndDownloadIcon(),代码文件:IcoTool.razor
@code { private async Task ConvertAndDownloadIcon() { if (_sourceBrowserFile ==
null) return; _loading = true; var tempSourcePath = Path.GetTempFileName(); var
tempDestPath = Path.GetTempFileName(); try { var fileName =
$"{Path.GetFileNameWithoutExtension(_sourceBrowserFile.Name)}.ico"; await
UploadFile(tempSourcePath); ImagingHelper.ConvertToIcon(tempSourcePath,
tempDestPath); await DownloadFile(tempDestPath, fileName); } finally {
DeleteFile(tempSourcePath); DeleteFile(tempDestPath); _loading = false; } }
private async Task UploadFile(string saveFilePath) { await using var
sourceFileStream = new FileStream(saveFilePath, FileMode.Create); await
_sourceBrowserFile!.OpenReadStream().CopyToAsync(sourceFileStream); } private
async Task DownloadFile(string fromFilePath, string saveFileName) { await using
var destFileStream = new FileStream(fromFilePath, FileMode.Open); using var
streamRef = new DotNetStreamReference(destFileStream); await
Js.InvokeVoidAsync("downloadFileFromStream", saveFileName, streamRef); } private
void DeleteFile(string filePath) { try { if (File.Exists(filePath))
File.Delete(filePath); } catch { // ignored } } }
The uploaded source files and converted Icon files will be temporarily saved during the conversion and download process, and these two files will be deleted immediately after the download is completed. Please use them with confidence.
2.3 Download of converted Ico file
How can I provide downloads after the file conversion is successful?
起初想使用一个<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>
页面下载时使用上面贴过的方法DownloadFile(string fromFilePath, string saveFileName),这里不再贴代码。
下载文件使用到JS互操作(什么是JS互操作?可以参考我转载的这篇文章了解(14/30)大家一起学 Blazor:JavaScript interop(互操作))
3. summary
- Blazor 组件库使用的MASA Blazor,很美观大方的
Material Design设计风格。 - Ico 转换,使用到了
System.Drawing.Common包的Bitmap,.NET 6 开始不支持跨平台,提示只支持Windows平台,重点:只是该包System.Drawing.Common不支持跨平台,不是.NET 6 不支持跨平台,.NET 6 支持跨平台。 - 本工具使用7.0.100-preview.1开发、编译、上线,使用.NET 6的同学,请放心使用,可以无缝升级。
Dotnet9工具箱会不断添加新的免费、开源、在线工具,欢迎 star 支持,有什么需求我会考虑加上,仓库地址:Dotnet9.Tools,可提交 issue、网站留言、微信公众号(dotnet9)联系等等。
本工具源码:IcoTool
介绍文章:Blazor 在线 Ico 转换工具
在线演示地址:https://tool.dotnet9.com/ico