ToB企服应用市场:ToB评测及商务社交产业平台

标题: 实现一个安全且高效的图片上传接口:利用ASP.NET Core和SHA256哈希 [打印本页]

作者: 商道如狼道    时间: 2025-2-14 13:20
标题: 实现一个安全且高效的图片上传接口:利用ASP.NET Core和SHA256哈希
实现一个安全且高效的图片上传接口:利用ASP.NET Core和SHA256哈希

在当代Web应用步调中,图片上传功能是常见的需求之一。无论是用户头像、产品图片照旧文档附件,确保文件上传的安全性和效率至关重要。本文将详细先容如何利用ASP.NET Core构建一个安全且高效的图片上传接口,并先容如何利用SHA256哈希算法避免重复文件存储。
项目背景

我们的目标是创建一个图片上传接口,支持以下特性:

技能栈


代码实现

1. 控制器界说

起首,我们界说了一个ImageUploadController类来处理图片上传哀求。下面是完整的控制器代码及其详细注释。
  1. using MES.Entity;
  2. using MES.Entity.Dtos.SystemDto.Response.UploadImage;
  3. using Microsoft.AspNetCore.Mvc;
  4. using System.Security.Cryptography;
  5. using System.IO;
  6. namespace MES.API.Controllers.SystemControllers
  7. {
  8.     /// <summary>
  9.     /// 图片上传控制器
  10.     /// </summary>
  11.     [Route("api/[controller]")]
  12.     [ApiController]
  13.     public class ImageUploadController : ControllerBase
  14.     {
  15.         /// <summary>
  16.         /// 日志记录器
  17.         /// </summary>
  18.         private readonly ILogger<ImageUploadController> _logger;
  19.         /// <summary>
  20.         /// 允许上传的文件类型
  21.         /// </summary>
  22.         private readonly string[] sourceArray = new[] { "image/jpeg", "image/png", "image/gif" };
  23.         /// <summary>
  24.         /// 静态文件根目录
  25.         /// </summary>
  26.         private readonly string StaticFileRoot = "wwwroot";
  27.         /// <summary>
  28.         /// 构造函数注入ILogger
  29.         /// </summary>
  30.         /// <param name="logger">日志记录器</param>
  31.         public ImageUploadController(ILogger<ImageUploadController> logger)
  32.         {
  33.             this._logger = logger;
  34.         }
  35.         /// <summary>
  36.         /// 上传图片方法
  37.         /// </summary>
  38.         /// <param name="file">图片文件</param>
  39.         /// <returns>上传结果</returns>
  40.         [HttpPost]
  41.         public async Task<IActionResult> UploadImageAsync(IFormFile file)
  42.         {
  43.             // 返回数据对象
  44.             ApiResult<UploadImageResponseDto> apiResult = new();
  45.             try
  46.             {
  47.                 // 检查文件类型是否合法
  48.                 if (!sourceArray.Contains(file.ContentType))
  49.                 {
  50.                     apiResult.Message = "图片格式不正确,请上传 jpg、png、gif 格式的图片!";
  51.                     return Ok(apiResult);
  52.                 }
  53.                 // 检查文件大小是否超过限制 (2MB)
  54.                 if (file.Length > 2 * 1024 * 1024)
  55.                 {
  56.                     apiResult.Message = "文件大小超过限制,请上传小于 2M 的图片!";
  57.                     return Ok(apiResult);
  58.                 }
  59.                 if (file.Length > 0)
  60.                 {
  61.                     // 获取文件名
  62.                     string fileName = Path.GetFileName(file.FileName);
  63.                     // 构造文件路径,按年月日分层存储
  64.                     string fileUrlWithoutFileName = $"InvoiceStaticFile/{DateTime.Now.Year}/{DateTime.Now.Month}/{DateTime.Now.Day}";
  65.                     string directoryPath = Path.Combine(StaticFileRoot, fileUrlWithoutFileName);
  66.                     // 创建文件夹,如果文件夹已存在,则什么也不做
  67.                     Directory.CreateDirectory(directoryPath);
  68.                     // 使用SHA256生成文件的唯一标识符
  69.                     using SHA256 hash = SHA256.Create();
  70.                     byte[] hashByte = await hash.ComputeHashAsync(file.OpenReadStream());
  71.                     string hashedFileName = BitConverter.ToString(hashByte).Replace("-", "");
  72.                     // 重新获得一个文件名
  73.                     string newFileName = hashedFileName + "." + fileName.Split('.').Last();
  74.                     string filePath = Path.Combine(directoryPath, newFileName);
  75.                     // 将文件写入指定路径
  76.                     await using FileStream fileStream = new(filePath, FileMode.Create);
  77.                     await file.CopyToAsync(fileStream);
  78.                     // 构造完整的URL以便前端使用
  79.                     string fullUrl = $"{Request.Scheme}://{Request.Host}/{fileUrlWithoutFileName}/{newFileName}";
  80.                     // 设置返回的数据
  81.                     apiResult.Data = new UploadImageResponseDto()
  82.                     {
  83.                         FilePath = fileUrlWithoutFileName,
  84.                         FileName = newFileName,
  85.                         FullPathName = Path.Combine(fileUrlWithoutFileName, newFileName)
  86.                     };
  87.                     apiResult.Message = "上传成功!";
  88.                     return Ok(apiResult);
  89.                 }
  90.                 apiResult.Message = "文件为空!请重新上传!";
  91.             }
  92.             catch (Exception ex)
  93.             {
  94.                 // 记录错误日志
  95.                 _logger.LogError("UploadImageAsync,上传图片失败,原因:{ErrorMessage}", ex.Message);
  96.                 apiResult.Code = ResponseCode.Code999;
  97.                 apiResult.Message = "一般性错误,请联系管理员!";
  98.             }
  99.             return Ok(apiResult);
  100.         }
  101.     }
  102. }
复制代码
2. 关键步骤分析

文件范例检查

我们起首检查上传文件的ContentType是否在允许的范围内(JPEG、PNG、GIF)。如果不在,则返回相应的错误信息。
  1. if (!sourceArray.Contains(file.ContentType))
  2. {
  3.     apiResult.Message = "图片格式不正确,请上传 jpg、png、gif 格式的图片!";
  4.     return Ok(apiResult);
  5. }
复制代码
文件巨细检查

为了防止大文件占用过多服务器资源,我们限制了上传文件的最大巨细(2MB)。
  1. if (file.Length > 2 * 1024 * 1024) // 限制文件大小不超过 2M
  2. {
  3.     apiResult.Message = "文件大小超过限制,请上传小于 2M 的图片!";
  4.     return Ok(apiResult);
  5. }
复制代码
利用SHA256生成唯一文件名

为了避免重复存储相同的文件,我们利用SHA256哈希算法生成唯一的文件名。
  1. using SHA256 hash = SHA256.Create();
  2. byte[] hashByte = await hash.ComputeHashAsync(file.OpenReadStream());
  3. string hashedFileName = BitConverter.ToString(hashByte).Replace("-", "");
  4. string newFileName = hashedFileName + "." + fileName.Split('.').Last();
复制代码
文件生存

我们将文件生存到指定路径,并构造完整的URL以便前端利用。
  1. string filePath = Path.Combine(directoryPath, newFileName);
  2. await using FileStream fileStream = new(filePath, FileMode.Create);
  3. await file.CopyToAsync(fileStream);
  4. string fullUrl = $"{Request.Scheme}://{Request.Host}/{fileUrlWithoutFileName}/{newFileName}";
复制代码
3. 错误处理与日志记录

在发生非常时,我们利用ILogger记录错误信息,并返回通用的错误消息给客户端。
  1. catch (Exception ex)
  2. {
  3.     _logger.LogError("UploadImageAsync,上传图片失败,原因:{ErrorMessage}", ex.Message);
  4.     apiResult.Code = ResponseCode.Code999;
  5.     apiResult.Message = "一般性错误,请联系管理员!";
  6. }
复制代码
总结

通过上述步骤,我们实现了一个高效且安全的图片上传接口。该接口不但可以或许验证文件范例和巨细,还可以或许避免重复存储相同的文件,提拔了系统的性能和用户体验。盼望这篇文章对你有所帮助!
如果你有任何问题或建议,请在评论区留言,我会尽力解答。

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4