SharpIcoWeb开发记录篇
SharpIcoWeb开发记录篇
前言
大佬用.NET 9.0开发了SharpIco轻量级图标生成工具,是一款控制台应用程序,支持AOT发布,非常方便。
功能特点
- ️ 将PNG图像转换为多尺寸ICO图标
- 支持生成包含自定义尺寸的ICO图标(最高支持1024×1024)
- 检查ICO文件的内部结构和信息
- 准确识别并显示超大尺寸图标(如512×512、1024×1024)的实际尺寸
直达地址:https://github.com/star-plan/sharp-ico
现在互联网上应该有很多这类小工具或者网站,我也想部署把这个小工具部署成网站,当然不要和别人网站一模一样,所有后端我采用 .NET Core Minimal API去开发。
大佬开发的这款小工具可以直接在集成 .NET Core中,所以我只需要写一个上传文件的接口就行了。用了 .NET这么久,写接口一直用的是 WebApi,这次尝试下 Minimal Api。
Minimal Api 简介
在使用 ASP.NET Core 生成快速 HTTP API 时,可以将最小 API 作为一种简化的方法。 可以使用最少的代码和配置生成完全正常运行的 REST 终结点。 跳过传统的基架,并通过流畅地声明 API 路由和操作来避免不必要的控制器。
主要特点
- 简洁的语法:使用更少的代码定义 API 端点
- 减少样板代码:不需要控制器类
- 内置依赖注入:简化服务配置
- 路由和请求处理一体化:直接在路由定义中处理请求
- 支持所有 ASP.NET Core 功能:中间件、认证、授权等
架构概述
MiniAPI的核心架构包括以下几个关键组件:
- WebApplication:这是整个应用的入口点和宿主。它负责配置服务、中间件和路由。
- Endpoints:这些是API的终点,也就是处理特定HTTP请求的地方。
- Handlers:这些是实际处理请求并生成响应的函数。
- Middleware:这些组件在请求到达handler之前和之后处理请求。
关键术语
在进行开发前,先了解下什么是 Endpoints、Handlers、Middleware。
Endpoints(端点):Endpoints是一个特定的URL路径,与一个HTTP方法相关联。
app.MapGet("/hello", () => "Hello, World!");
Handlers(处理器):Handlers是一个函数,它接收http请求并返回响应。在
MiniAPIs中handler可以是一个简单的lambda表达式,也可以是一个单独定义的方法。例如:app.MapGet("/users/{id}", (int id) => $"User ID: {id}");Middleware(中间件):与WebApi一样,MiniAPIs中也可以使用中间件,它们可以在请求到达handler之前执行操作,也可以在handler处理完请求后修改响应。例如,你可以使用中间件来处理身份验证、日志记录或异常处理。
开发
了解完一些 Minimal Api基础知识后,就可以开始开发了。首先创建 MiniMal Api模版的 .NET 9 Api程序。
模版默认为创建一个示例接口,看到这个接口,看起来和 WebApi写法差别不大,但是代码是直接写在 Program.cs文件中,无需创建控制器。

接下来开发一个上传文件的接口,然后调用大佬开发的工具就ok了。
在 MiniAPI中也可以使用依赖注入,那么这里创建一个处理文件的服务,然后再注入这个服务在接口中使用。
项目结构
可以看到我的项目可以直接引用 SharpIco工具,这里右键 SharpIco编辑csproj文件将项目类型改为类库就可以直接调用了。
<PropertyGroup>
<!-- 改为生成库而不是控制台应用 -->
<OutputType>Library</OutputType>
</PropertyGroup>

IFileService接口
public interface IFileService
{
// 检查文件是否有效
bool IsFileValid(IFormFile file);
// 保存上传的文件
Task<string> SaveUploadedFile(IFormFile file);
// 创建临时目录
string GetTempDirectory();
// 读取文件到内存流
Task<MemoryStream> ReadFileToMemoryAsync(string filePath);
// 删除临时文件
void DeleteFile(string tempFilePath);
}
FileService类
这里使用了一个变量 _tempFiles去记录上传的临时文件,然后实现IDisposable接口自动清理临时文件,确保上传的图片不会留存到硬盘里面。
一开始我的设计思路就是在 wooroot中去建立一个待转换和转换后的目录取处理图片,但转念一想,作为一款图片转ico的工具,还是不要留存图片好一点,所以才采用了临时目录这个办法。
public class FileService : IFileService, IDisposable
{
private readonly List<string> _tempFiles = new();
public string GetTempDirectory()
{
var tempDir = Path.Combine(Path.GetTempPath(), "SharpIcoTemp");
Directory.CreateDirectory(tempDir);
return tempDir;
}
public bool IsFileValid(IFormFile file)
{
var allowedExtensions = new[] { ".png", ".jpg", ".jpeg" };
var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
return file.Length is > 0 and < 10 * 1024 * 1024 && // 10MB
allowedExtensions.Contains(extension);
}
// 保存上传的文件
public async Task<string> SaveUploadedFile(IFormFile file)
{
var tempDir = GetTempDirectory();
var tempFilePath = Path.Combine(tempDir, $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}");
await using var stream = new FileStream(tempFilePath, FileMode.Create);
await file.CopyToAsync(stream);
_tempFiles.Add(tempFilePath); // 记录待清理文件
return tempFilePath;
}
// 安全读取文件到内存流
public async Task<MemoryStream> ReadFileToMemoryAsync(string filePath)
{
var memoryStream = new MemoryStream();
await using (var fileStream = File.OpenRead(filePath))
{
await fileStream.CopyToAsync(memoryStream);
}
memoryStream.Position = 0;
return memoryStream;
}
public void DeleteFile(string tempFilePath)
{
if (File.Exists(tempFilePath))
File.Delete(tempFilePath);
}
// 实现IDisposable接口自动清理
public void Dispose()
{
foreach (var file in _tempFiles)
{
try
{
if (File.Exists(file))
File.Delete(file);
}
catch { /* 忽略删除异常 */ }
}
GC.SuppressFinalize(this);
}
}
Program类
服务写完后,接下来在配置类中注入服务并完成接口编写就大功告成了。
注入服务:
builder.Services.AddScoped<IFileService, FileService>();
接口编写:
app.MapPost("/api/uploadDownload", async ([FromForm] IFormFile file,[FromForm] string? sizes,IFileService fileService, ILogger<Program> logger) =>
{
try
{
// 验证输入
if (!fileService.IsFileValid(file))
{
return Results.BadRequest("请上传有效文件,文件大小不能超过10MB");
}
// 保存临时文件
var tempFilePath = await fileService.SaveUploadedFile(file);
// 生成输出路径
var outputPath = Path.Combine(fileService.GetTempDirectory(), $"{Guid.NewGuid()}.ico");
// 执行转换 处理大小参数
var sizesArray = sizes?.Split(',').Select(int.Parse).ToArray();
if (sizesArray is { Length: > 0 })
IcoGenerator.GenerateIcon(tempFilePath, outputPath, sizesArray);
else
IcoGenerator.GenerateIcon(tempFilePath, outputPath);
// 读取到内存
var memoryStream = await fileService.ReadFileToMemoryAsync(outputPath);
// 删除临时文件
fileService.DeleteFile(outputPath);
logger.LogInformation($"{DateTime.UtcNow} 文件 {file.FileName} 转换成功");
return Results.File(memoryStream, "image/x-icon");
}
catch (Exception ex)
{
logger.LogError(ex, $"{DateTime.UtcNow} 处理文件时发生错误");
return Results.Problem("处理文件时发生错误");
}
}).DisableAntiforgery();
接口测试
这里直接使用模版自带的http文件进行测试,这个还是第一次使用,还折腾了挺久。
SharpIcoWeb.http 文件
简单介绍下,第一个请求只上传了图片文件,未指定尺寸参数,后端接口会默认生成16,32,48,64,128,256,512,1024 尺寸的ico文件。
第二个请求就是添加了尺寸参数的请求。
@SharpIcoWeb_HostAddress = http://localhost:5235
### 上传文件并转换为ICO(不带尺寸参数)
POST {{SharpIcoWeb_HostAddress}}/api/uploadDownload
Content-Type: multipart/form-data; boundary=WebAppBoundary
--WebAppBoundary
Content-Disposition: form-data; name="file"; filename="1.png"
Content-Type: image/png
< ./1.png
--WebAppBoundary--
### 上传文件并转换为ICO(带尺寸参数)
POST {{SharpIcoWeb_HostAddress}}/api/uploadDownload
Content-Type: multipart/form-data; boundary=WebAppBoundary
--WebAppBoundary
Content-Disposition: form-data; name="file"; filename="1.png"
Content-Type: image/png
< ./1.png
--WebAppBoundary
Content-Disposition: form-data; name="sizes"
16,32,48,64,128
测试结果

关键代码
IcoGenerator.GenerateIcon(tempFilePath, outputPath, sizesArray);这里是调用SharpIco工具提供的方法。
app.MapPost().DisableAntiforgery()DisableAntiforgery作用是禁用 ASP.NET Core 的防伪造令牌验证。
相关链接
- SharpIco:https://github.com/star-plan/sharp-ico
- SharpIcoWeb:https://github.com/ZyPLJ/SharpIcoWeb
- 最小API官方文档:https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/minimal-apis/overview?view=aspnetcore-9.0
- 最小API学习指南:https://blog.csdn.net/xiaohucxy/article/details/140134927
SharpIcoWeb开发记录篇的更多相关文章
- Python全栈开发记录_第一篇(循环练习及杂碎的知识点)
Python全栈开发记录只为记录全栈开发学习过程中一些难和重要的知识点,还有问题及课后题目,以供自己和他人共同查看.(该篇代码行数大约:300行) 知识点1:优先级:not>and 短路原则:a ...
- .Net Core ORM选择之路,哪个才适合你 通用查询类封装之Mongodb篇 Snowflake(雪花算法)的JavaScript实现 【开发记录】如何在B/S项目中使用中国天气的实时天气功能 【开发记录】微信小游戏开发入门——俄罗斯方块
.Net Core ORM选择之路,哪个才适合你 因为老板的一句话公司项目需要迁移到.Net Core ,但是以前同事用的ORM不支持.Net Core 开发过程也遇到了各种坑,插入条数多了也特别 ...
- 【学习记录】第一章 数据库设计-《SQL Server数据库设计和开发基础篇视频课程》
一.课程笔记 1.1 软件开发周期 (1)需求分析阶段 分析客户的业务和数据处理需求. (2)概要设计阶段 设计数据库的E-R模型图,确认需求信息的正确和完整. /* E-R图:实体-关系图(Ent ...
- CozyRSS开发记录12-MVVM,绑定RSS源和数据
CozyRSS开发记录12-MVVM,绑定RSS源和数据 1.引入MvvmLight MVVM最近貌似在前端那块也挺火的.据说,WPF的程序如果不用MVVM,那跟MFC和winform的,也没啥区别. ...
- iOS开发数据库篇—SQLite简单介绍
iOS开发数据库篇—SQLite简单介绍 一.离线缓存 在项目开发中,通常都需要对数据进行离线缓存的处理,如新闻数据的离线缓存等. 说明:离线缓存一般都是把数据保存到项目的沙盒中.有以下几种方式 (1 ...
- iOS开发数据库篇—SQL
iOS开发数据库篇—SQL 一.SQL语句 如果要在程序运行过程中操作数据库中的数据,那得先学会使用SQL语句 1.什么是SQL SQL(structured query language):结构化查 ...
- iOS开发数据库篇—SQL代码应用示例
iOS开发数据库篇—SQL代码应用示例 一.使用代码的方式批量添加(导入)数据到数据库中 1.执行SQL语句在数据库中添加一条信息 插入一条数据的sql语句: 点击run执行语句之后,刷新数据 2.在 ...
- iOS开发数据库篇—SQLite的应用
iOS开发数据库篇—SQLite的应用 一.简单说明 在iOS中使用SQLite3,首先要添加库文件libsqlite3.dylib和导入主头文件. 导入头文件,可以使用库中的函数(是纯C语言的) 二 ...
- IOS开发数据库篇—SQLite模糊查询
IOS开发数据库篇—SQLite模糊查询 一.示例 说明:本文简单示例了SQLite的模糊查询 1.新建一个继承自NSObject的模型 该类中的代码: // // YYPerson.h // 03- ...
- iOS开发数据库篇—SQLite常用的函数
iOS开发数据库篇—SQLite常用的函数 一.简单说明 1.打开数据库 int sqlite3_open( const char *filename, // 数据库的文件路径 sqlite3 * ...
随机推荐
- leetcode每日一题:对角线上不同值的数量差
题目 2711. 对角线上不同值的数量差 给你一个下标从 0 开始.大小为 m x n 的二维矩阵 grid ,请你求解大小同样为 m x n 的答案矩阵 answer . 矩阵 answer 中每个 ...
- AI可解释性 II | Saliency Maps-based 归因方法(Attribution)论文导读(持续更新)
AI可解释性 II | Saliency Maps-based 归因方法(Attribution)论文导读(持续更新) 导言 本文作为AI可解释性系列的第二部分,旨在以汉语整理并阅读归因方法(Attr ...
- 华为od机考2025A卷真题 -查找接口成功率最优时间段
题目描述与示例 题目描述 服务之间交换的接口成功率作为服务调用关键质量特性,某个时间段内的接口失败率使用一个数组表示,数组中每个元素都是单位时间内失败率数值,数组中的数值为 0~100 的整数,给定一 ...
- Python requests代理(Proxy)使用教程
Python requests代理(Proxy)使用教程 在 Python 的 requests 库中,使用代理服务器可以让你通过不同的网络路由发送 HTTP 请求.代理服务器可以帮助隐藏真实 IP ...
- apache配置symfony并隐藏入口文件app.php
------------------------------- 参考: 配置Web服务器 apache url路由配置重写 Apache URL重写规则(详解) symfony官网文档 ------- ...
- 10个 DeepSeek 神级提示词,建议收藏!
在当下人工智能飞速发展的时代,DeepSeek 作为一款功能强大的 AI 工具,能够帮助我们实现各种创意和需求.然而,要充分发挥它的潜力,掌握一些巧妙的提示词至关重要.今天,就为大家精心整理了 15 ...
- [开源] .Net 使用 ORM 访问 人大金仓数据库
前言 京人大金仓信息技术股份有限公司(以下简称"人大金仓")是具有自主知识产权的国产数据管理软件与服务提供商.人大金仓由中国人民大学一批最早在国内开展数据库教学.科研.开发的专家于 ...
- 松灵机器人scout mini 自主导航(5)——采用CMU团队导航策略
重操旧业,最近实验室又需要测试无人车导航算法,因此又重新启动了松灵机器人scout mini小车 自主导航项目.通过调研,最终选择了前几年比较火的CMU团队的策略(https://www.cmu-ex ...
- 探索 Rust:从基础语法到实用概念
@charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...
- 把iview的table做成更适合展现大量数据的样式(字体变小、去除多余的padding等)
<style> .ivu-table { font-size: 12px !important; } .ivu-table-header thead tr th { padding: 0p ...