实现一个安全且高效的图片上传接口:使用ASP.NET Core和SHA256哈希

news/2025/2/3 20:39:41/

实现一个安全且高效的图片上传接口:使用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 = "一般性错误,请联系管理员!";
}

总结

通过上述步骤,我们实现了一个高效且安全的图片上传接口。该接口不仅能够验证文件类型和大小,还能够避免重复存储相同的文件,提升了系统的性能和用户体验。希望这篇文章对你有所帮助!

如果你有任何问题或建议,请在评论区留言,我会尽力解答。


希望这篇更新后的博客文章对你有帮助!你可以根据实际需求进一步调整和完善内容。如果你有更多具体的需求或者想要添加的内容,随时告诉我!


http://www.ppmy.cn/news/1569044.html

相关文章

一文读懂 HTTP:Web 数据交换的基石

HTTP 概述 HTTP 是一种用作获取诸如 HTML 文档这类资源的协议。它是 Web 上进行任何数据交换的基础&#xff0c;同时&#xff0c;也是一种客户端—服务器&#xff08;client-server&#xff09;协议&#xff0c;也就是说&#xff0c;请求是由接受方——通常是 Web 浏览器——发…

每日 Java 面试题分享【第 15 天】

欢迎来到每日 Java 面试题分享栏目&#xff01; 订阅专栏&#xff0c;不错过每一天的练习 今日分享 3 道面试题目&#xff01; 评论区复述一遍印象更深刻噢~ 目录 问题一&#xff1a;Java 中的基本数据类型有哪些&#xff1f;问题二&#xff1a;什么是 Java 中的自动装箱和…

计算机网络三张表(ARP表、MAC表、路由表)总结

参考&#xff1a; 网络三张表&#xff1a;ARP表, MAC表, 路由表&#xff0c;实现你的网络自由&#xff01;&#xff01;_mac表、arp表、路由表-CSDN博客 网络中的三张表&#xff1a;ARP表、MAC表、路由表 首先要明确一件事&#xff0c;如果一个主机要发送数据&#xff0c;那么必…

4 [危机13小时追踪一场GitHub投毒事件]

事件概要 自北京时间 2024.12.4 晚间6点起&#xff0c; GitHub 上不断出现“幽灵仓库”&#xff0c;仓库中没有任何代码&#xff0c;只有诱导性的病毒文件。当天&#xff0c;他们成为了 GitHub 上 star 增速最快的仓库。超过 180 个虚假僵尸账户正在传播病毒&#xff0c;等待不…

蓝桥杯思维训练营(二)

文章目录 题目总览题目详解小S按按钮小R排数字 牛客小白月赛108 小S按按钮 小R排数字 题目总览 题目详解 小S按按钮 思路分析&#xff1a;正常处理即可&#xff0c;但是对于x0的情况要特别处理&#xff0c;是奇数次的时候可以解决 import math T int(input()) for _ in ra…

Unity实现按键设置功能代码

一、前言 最近在学习unity2D&#xff0c;想做一个横版过关游戏&#xff0c;需要按键设置功能&#xff0c;让用户可以自定义方向键与攻击键等。 自己写了一个&#xff0c;总结如下。 二、界面效果图 这个是一个csv文件&#xff0c;准备第一列是中文按键说明&#xff0c;第二列…

100.3 AI量化面试题:解释配对交易(Pairs Trading)的原理,并说明如何选择配对股票以及设计交易信号

目录 0. 承前1. 配对交易基本原理1.1 什么是配对交易1.2 基本假设 2. 配对选择方法2.1 相关性分析2.2 协整性检验 3. 价差计算方法3.1 简单价格比率3.2 回归系数法 4. 交易信号设计4.1 标准差方法4.2 动态阈值方法 5. 风险管理5.1 止损设计5.2 仓位管理 6. 策略评估6.1 回测框架…

python:洛伦兹变换

洛伦兹变换&#xff08;Lorentz transformations&#xff09;是相对论中的一个重要概念&#xff0c;特别是在讨论时空的变换时非常重要。在四维时空的背景下&#xff0c;洛伦兹变换描述了在不同惯性参考系之间如何变换时间和空间坐标。在狭义相对论中&#xff0c;洛伦兹变换通常…