在 Web 应用程序开发过程中,总是无法避免涉及到文件上传,这次我们来聊一聊怎么去实现一个简单方便可复用文件上传功能;通过创建自定义绑定模型来实现文件上传。

  • 1.1 在 Asp.Net Core MVC 中,内置了很多种绑定模型,让我们可以很方便的去使用,比如下面常用的几种绑定模型
  1. FromBodyAttribute
  2. FromFromAttribute
  3. FromQueryAttribute
  4. FromHeaderAttribute
  5. FromServicesAttribute
  6. FromRouteAttribute
  • 常见用法比如
  1. [HttpPost]
  2. public async Task<IActionResult> PostInfo([FromBody]UserInfo user,[FromQuery] string city)
  3. {
  4. ...
  5. }
    1. 查看以上绑定模型,唯独缺少一个 FromFileAttribute ,下面就来实现一个自己的 FromFileAttribute
  1. public class FromFileAttribute : Attribute, IBindingSourceMetadata
  2. {
  3. public BindingSource BindingSource => BindingSource.FormFile;
  4. }
  • 非常简单,就三行代码,完全照抄系统内置的绑定模型,唯一不同的就是指定 BindingSource 为 BindingSource.FormFile。
  • 2.1 创建 UserFile
  1. public class UserFile
  2. {
  3. public string FileName { get; set; }
  4. public long Length { get; set; }
  5. public string Extension { get; set; }
  6. public string FileType { get; set; }
  7. private readonly static string[] Filters = { ".jpg", ".png", ".bmp" };
  8. public bool IsValid => !string.IsNullOrEmpty(this.Extension) && Filters.Contains(this.Extension);
  9. private IFormFile file;
  10. public IFormFile File
  11. {
  12. get { return file; }
  13. set
  14. {
  15. if (value != null)
  16. {
  17. this.file = value;
  18. this.FileType = this.file.ContentType;
  19. this.Length = this.file.Length;
  20. this.Extension = this.file.FileName.Substring(file.FileName.LastIndexOf('.'));
  21. if (string.IsNullOrEmpty(this.FileName))
  22. this.FileName = this.FileName;
  23. }
  24. }
  25. }
  26. public async Task<string> SaveAs(string destinationDir = null)
  27. {
  28. if (this.file == null)
  29. throw new ArgumentNullException("没有需要保存的文件");
  30. if (destinationDir != null)
  31. Directory.CreateDirectory(destinationDir);
  32. var newName = DateTime.Now.Ticks;
  33. var newFile = Path.Combine(destinationDir ?? "", $"{newName}{this.Extension}");
  34. using (FileStream fs = new FileStream(newFile, FileMode.CreateNew))
  35. {
  36. await this.file.CopyToAsync(fs);
  37. fs.Flush();
  38. }
  39. return newFile;
  40. }
  41. }
  • UserFile 是一个带保持文件行为的实体类,该类的公共属性用于从表单域中接收和属性名称相同的表单值,其中公共属性 File 用于接收文件,并在设置值的时候去做一些其它属性初始化的工作,比如文件长度和扩展名、文件类型
  • 其中还实现了一个简单的文件过滤器,判断客户端上传的文件是否属于服务端允许上传的文件扩展名
  • 最后 SaveAs(string destinationDir = null) 通过传入指定目录,将文件保存,并返回保存后的文件绝对路径
  • 3.1 下面就定义一个简单的 API 接口,用于测试上传文件
  1. [HttpPost]
  2. public async Task<IActionResult> Post([FromFile]UserFile file)
  3. {
  4. if (file == null || !file.IsValid)
  5. return new JsonResult(new { code = 500, message = "不允许上传的文件类型" });
  6. string newFile = string.Empty;
  7. if (file != null)
  8. newFile = await file.SaveAs("/data/files/images");
  9. return new JsonResult(new { code = 0, message = "成功", url = newFile });
  10. }
  • 3.2 首先是在 Post([FromFile]UserFile file) 中使用上面创建的 FromFileAttribute 对模型 UserFile 进行绑定,然后验证文件是否正确,接下来通过 file.SaveAs(“/data/files/images”); 保存文件

  • 3.3 上传代码非常简单,几乎到了无法精简的程度,最终发挥作用的就是 file.SaveAs 操作

  • 4.1 现在通过控制台启动服务

  • 4.2 使用 Postman 模拟表单上传文件

  • 4.3 上传成功,现在来查看目录下是否有文件

  • 在上传表单中,我们定义了附件的名称为 file 对应绑定模型的公共属性 File,这样模型就可以自动获得该文件
  • 表单中还传递了另外一个字段 filename,对应绑定模型的公共属性 FileName,实现自定义文件友好显示名称
  • 通过自定义模型绑定,实现了快速上传文件功能,该功能只能用于上传小文件,对于大文件,还是需要实现分片上传,或者使用 CDN 等服务商的接口

版权声明:本文为viter原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/viter/p/10074766.html