实现一个安全且高效的图片上传接口:使用ASP.NET Core和SHA256哈希
实现一个安全且高效的图片上传接口:使用ASP.NET Core和SHA256哈希
在现代Web应用程序中,图片上传功能是常见的需求之一。无论是用户头像、产品图片还是文档附件,确保文件上传的安全性和效率至关重要。本文将详细介绍如何使用ASP.NET Core构建一个安全且高效的图片上传接口,并介绍如何利用SHA256哈希算法避免重复文件存储。
项目背景
我们的目标是创建一个图片上传接口,支持以下特性:
- 支持多种图片格式(JPEG、PNG、GIF)
- 文件大小限制(不超过2MB)
- 避免重复文件存储
- 返回友好的错误消息
技术栈
- .NET 8: 提供强大的API开发框架。
- IFormFile: 用于处理上传的文件。
- SHA256: 用于生成文件的唯一标识符,避免重复存储相同内容的文件。
- NLog/ILogger: 用于日志记录。
代码实现
1. 控制器定义
首先,我们定义了一个ImageUploadController类来处理图片上传请求。下面是完整的控制器代码及其详细注释。
using MES.Entity;
using MES.Entity.Dtos.SystemDto.Response.UploadImage;
using Microsoft.AspNetCore.Mvc;
using System.Security.Cryptography;
using System.IO;namespace MES.API.Controllers.SystemControllers
{/// <summary>/// 图片上传控制器/// </summary>[Route("api/[controller]")][ApiController]public class ImageUploadController : ControllerBase{/// <summary>/// 日志记录器/// </summary>private readonly ILogger<ImageUploadController> _logger;/// <summary>/// 允许上传的文件类型/// </summary>private readonly string[] sourceArray = new[] { "image/jpeg", "image/png", "image/gif" };/// <summary>/// 静态文件根目录/// </summary>private readonly string StaticFileRoot = "wwwroot";/// <summary>/// 构造函数注入ILogger/// </summary>/// <param name="logger">日志记录器</param>public ImageUploadController(ILogger<ImageUploadController> logger){this._logger = logger;}/// <summary>/// 上传图片方法/// </summary>/// <param name="file">图片文件</param>/// <returns>上传结果</returns>[HttpPost]public async Task<IActionResult> UploadImageAsync(IFormFile file){// 返回数据对象ApiResult<UploadImageResponseDto> apiResult = new();try{// 检查文件类型是否合法if (!sourceArray.Contains(file.ContentType)){apiResult.Message = "图片格式不正确,请上传 jpg、png、gif 格式的图片!";return Ok(apiResult);}// 检查文件大小是否超过限制 (2MB)if (file.Length > 2 * 1024 * 1024) {apiResult.Message = "文件大小超过限制,请上传小于 2M 的图片!";return Ok(apiResult);}if (file.Length > 0){// 获取文件名string fileName = Path.GetFileName(file.FileName); // 构造文件路径,按年月日分层存储string fileUrlWithoutFileName = $"InvoiceStaticFile/{DateTime.Now.Year}/{DateTime.Now.Month}/{DateTime.Now.Day}"; string directoryPath = Path.Combine(StaticFileRoot, fileUrlWithoutFileName);// 创建文件夹,如果文件夹已存在,则什么也不做Directory.CreateDirectory(directoryPath);// 使用SHA256生成文件的唯一标识符using SHA256 hash = SHA256.Create();byte[] hashByte = await hash.ComputeHashAsync(file.OpenReadStream());string hashedFileName = BitConverter.ToString(hashByte).Replace("-", "");// 重新获得一个文件名string newFileName = hashedFileName + "." + fileName.Split('.').Last();string filePath = Path.Combine(directoryPath, newFileName);// 将文件写入指定路径await using FileStream fileStream = new(filePath, FileMode.Create);await file.CopyToAsync(fileStream);// 构造完整的URL以便前端使用string fullUrl = $"{Request.Scheme}://{Request.Host}/{fileUrlWithoutFileName}/{newFileName}";// 设置返回的数据apiResult.Data = new UploadImageResponseDto(){FilePath = fileUrlWithoutFileName,FileName = newFileName,FullPathName = Path.Combine(fileUrlWithoutFileName, newFileName)};apiResult.Message = "上传成功!";return Ok(apiResult);}apiResult.Message = "文件为空!请重新上传!";}catch (Exception ex){// 记录错误日志_logger.LogError("UploadImageAsync,上传图片失败,原因:{ErrorMessage}", ex.Message);apiResult.Code = ResponseCode.Code999;apiResult.Message = "一般性错误,请联系管理员!";}return Ok(apiResult);}}
}
2. 关键步骤解析
文件类型检查
我们首先检查上传文件的ContentType是否在允许的范围内(JPEG、PNG、GIF)。如果不在,则返回相应的错误信息。
if (!sourceArray.Contains(file.ContentType))
{apiResult.Message = "图片格式不正确,请上传 jpg、png、gif 格式的图片!";return Ok(apiResult);
}
文件大小检查
为了防止大文件占用过多服务器资源,我们限制了上传文件的最大大小(2MB)。
if (file.Length > 2 * 1024 * 1024) // 限制文件大小不超过 2M
{apiResult.Message = "文件大小超过限制,请上传小于 2M 的图片!";return Ok(apiResult);
}
使用SHA256生成唯一文件名
为了避免重复存储相同的文件,我们使用SHA256哈希算法生成唯一的文件名。
using SHA256 hash = SHA256.Create();
byte[] hashByte = await hash.ComputeHashAsync(file.OpenReadStream());
string hashedFileName = BitConverter.ToString(hashByte).Replace("-", "");
string newFileName = hashedFileName + "." + fileName.Split('.').Last();
文件保存
我们将文件保存到指定路径,并构造完整的URL以便前端使用。
string filePath = Path.Combine(directoryPath, newFileName);
await using FileStream fileStream = new(filePath, FileMode.Create);
await file.CopyToAsync(fileStream);
string fullUrl = $"{Request.Scheme}://{Request.Host}/{fileUrlWithoutFileName}/{newFileName}";
3. 错误处理与日志记录
在发生异常时,我们使用ILogger记录错误信息,并返回通用的错误消息给客户端。
catch (Exception ex)
{_logger.LogError("UploadImageAsync,上传图片失败,原因:{ErrorMessage}", ex.Message);apiResult.Code = ResponseCode.Code999;apiResult.Message = "一般性错误,请联系管理员!";
}
总结
通过上述步骤,我们实现了一个高效且安全的图片上传接口。该接口不仅能够验证文件类型和大小,还能够避免重复存储相同的文件,提升了系统的性能和用户体验。希望这篇文章对你有所帮助!
如果你有任何问题或建议,请在评论区留言,我会尽力解答。
希望这篇更新后的博客文章对你有帮助!你可以根据实际需求进一步调整和完善内容。如果你有更多具体的需求或者想要添加的内容,随时告诉我!
相关文章:
实现一个安全且高效的图片上传接口:使用ASP.NET Core和SHA256哈希
实现一个安全且高效的图片上传接口:使用ASP.NET Core和SHA256哈希 在现代Web应用程序中,图片上传功能是常见的需求之一。无论是用户头像、产品图片还是文档附件,确保文件上传的安全性和效率至关重要。本文将详细介绍如何使用ASP.NET Core构建…...
PyTorch中的movedim、transpose与permute
在PyTorch中,movedim、transpose 和 permute这三个操作都可以用来重新排列张量(tensor)的维度,它们功能相似却又有所不同。 movedim 🔗 torch.movedim 用途:将张量的一个或多个维度移动到新的位置。参数&…...
HTTP(1)
HTTP协议 HTTP是什么 HTTP(全称为"超文本传输协议")是一种应用非常广泛的基于TCP协议的应用层协议。 常见的应用场景: 浏览器与服务器之间的交互(访问网站)手机与服务器之间的通信多个服务器之间进行通信 …...
C#常考随笔2:函数中多次使用string的+=处理,为什么会产生大量内存垃圾(垃圾碎片),有什么好的方法可以解决?
在 C# 中,由于string类型是不可变的,当在函数中多次使用操作符来拼接字符串时,每次操作都会创建一个新的string对象,旧的对象则成为垃圾对象,这会导致大量的内存分配和垃圾回收,产生内存垃圾和碎片。 在需…...
leetcode刷题记录(一百)——121. 买卖股票的最佳时机
(一)问题描述 121. 买卖股票的最佳时机 - 力扣(LeetCode)121. 买卖股票的最佳时机 - 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并…...
MATLAB绘图时线段颜色、数据点形状与颜色等设置,介绍
MATLAB在绘图时,设置线段颜色和数据点的形状与颜色是提高图形可读性与美观性的重要手段。本文将详细介绍如何在 MATLAB 中设置这些属性。 文章目录 线段颜色设置单字母颜色表示法RGB 值表示法 数据点的形状与颜色设置设置数据点颜色和形状示例代码 运行结果小结 线段…...
CIMRTS材质美化--放大采样、缩小采样
最新的CIMRTS v1.0.10中在要素管理中的材质美化增加「放大采样」和「缩小采样」参数,对于透明树叶可以达到较好效果。 在CesiumLab中,一棵树处理完成后,在EarthSDK中,就是呈现这样缩小就会有树叶丢失的情况。效果如下:…...
P8738 [蓝桥杯 2020 国 C] 天干地支
两种方法 #include<bits/stdc.h> using namespace std;int main(){int year;cin>>year;string tg[10] {"geng", "xin", "ren", "gui","jia", "yi", "bing", "ding", "wu&…...
PyCharm接入DeepSeek实现AI编程
目录 效果演示 创建API key 在PyCharm中下载CodeGPT插件 配置Continue DeepSeek 是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的 AI 模型。DeepSeek-V3 是 DeepSeek 公司推出的最新一代 AI 模型。其前身是 DeepSeek-V2.5,经过持续的…...
Java编程语言:辉煌的历史与未来前景
如果将软件开发世界比喻成一个宇宙,Java 无疑是其中最亮的星星之一。它从诞生起就改变了软件开发世界的格局。发展到今天,Java仍然是这个世界上最重要的编程语言之一。当然,它也面临着新的挑战。 Java的诞生 回溯到 1991 年,在 …...
麦田物语学习笔记:保存和加载场景中的物品
目录 基本流程 1.代码思路 2.代码实现 最终效果 补充知识点 1.序列化 2.委托 基本流程 现在在切换场景后,场景中的物品即使被拾取了,也还是会被重新加载出来,所以本篇文章的任务是在切换场景前后能保留当前场景的数据 1.代码思路 (1)为了保留处在地上的物品数据,就需要…...
页高速缓存与缓冲区缓存的应用差异
页高速缓存(Page Cache)与缓冲区缓存(Buffer Cache)是计算机系统中用于提高数据访问性能的两种不同类型的缓存机制,它们的差异主要体现在以下几个方面: 缓存目的 页高速缓存:主要用于加速对磁…...
深度学习 Pytorch 单层神经网络
神经网络是模仿人类大脑结构所构建的算法,在人脑里,我们有轴突连接神经元,在算法中,我们用圆表示神经元,用线表示神经元之间的连接,数据从神经网络的左侧输入,让神经元处理之后,从右…...
一文读懂 HTTP:Web 数据交换的基石
HTTP 概述 HTTP 是一种用作获取诸如 HTML 文档这类资源的协议。它是 Web 上进行任何数据交换的基础,同时,也是一种客户端—服务器(client-server)协议,也就是说,请求是由接受方——通常是 Web 浏览器——发…...
算法知识补充2
一部分:Tire树:高效地存储和查找字符串集合的数据结构acwing835 #include<iostream> #include<cstring> using namespace std; const int N100010; int son[N][26],cnt[N],idx; char str[N]; void insert(char str[]){int p0;for(int i0;st…...
Vue.js组件开发-实现对视频预览
在 Vue 中实现视频文件预览 实现步骤 创建 Vue 组件:构建一个 Vue 组件用于处理视频文件的选择和预览。文件选择:添加一个文件输入框,允许用户选择视频文件。读取文件:监听文件选择事件,使用 FileReader API 读取所选…...
SSM开发(三) spring与mybatis整合(含完整运行demo源码)
目录 本文主要内容 一、Spring整合MyBatis的三个关键点 二、整合步骤 1、创建一个Maven项目 2、在pom.xml文件中添加jar包的依赖 3、配置MyBatis 注解实现方式 XML配置文件实现 4、配置Spring 5、测试运行 本文主要内容 1. Spring + Mybatis整合; 2. MyBatis两种SQL…...
.NET MAUI进行UDP通信(二)
上篇文章有写过一个简单的demo,本次对项目进行进一步的扩展,添加tabbar功能。 1.修改AppShell.xaml文件,如下所示: <?xml version"1.0" encoding"UTF-8" ?> <Shellx:Class"mauiDemo.AppShel…...
14-6-3C++STL的list
(一)list的插入 1.list.insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置 #include <iostream> #include <list> using namespace std; int main() { list<int> lst; lst.push_back(10); l…...
AAAI2024论文解读|HGPROMPT Bridging Homogeneous and Heterogeneous Graphs
论文标题 HGPROMPT: Bridging Homogeneous and Heterogeneous Graphs for Few-shot Prompt Learning 跨同构异构图的小样本提示学习 论文链接 HGPROMPT: Bridging Homogeneous and Heterogeneous Graphs for Few-shot Prompt Learning论文下载 论文作者 Xingtong Yu, Yuan…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...
