在 Web 應用程式開發過程中,總是無法避免涉及到文件上傳,這次我們來聊一聊怎麼去實現一個簡單方便可復用文件上傳功能;通過創建自定義綁定模型來實現文件上傳。 ...
前言
在 Web 應用程式開發過程中,總是無法避免涉及到文件上傳,這次我們來聊一聊怎麼去實現一個簡單方便可復用文件上傳功能;通過創建自定義綁定模型來實現文件上傳。
1. 實現自定義綁定模型
- 1.1 在 Asp.Net Core MVC 中,內置了很多種綁定模型,讓我們可以很方便的去使用,比如下麵常用的幾種綁定模型
FromBodyAttribute
FromFromAttribute
FromQueryAttribute
FromHeaderAttribute
FromServicesAttribute
FromRouteAttribute
- 常見用法比如
[HttpPost]
public async Task<IActionResult> PostInfo([FromBody]UserInfo user,[FromQuery] string city)
{
...
}
- 查看以上綁定模型,唯獨缺少一個 FromFileAttribute ,下麵就來實現一個自己的 FromFileAttribute
public class FromFileAttribute : Attribute, IBindingSourceMetadata
{
public BindingSource BindingSource => BindingSource.FormFile;
}
- 非常簡單,就三行代碼,完全照抄系統內置的綁定模型,唯一不同的就是指定 BindingSource 為 BindingSource.FormFile。
2. 實現一個上傳文件實體類,專門用於接收客戶端參數
- 2.1 創建 UserFile
public class UserFile
{
public string FileName { get; set; }
public long Length { get; set; }
public string Extension { get; set; }
public string FileType { get; set; }
private readonly static string[] Filters = { ".jpg", ".png", ".bmp" };
public bool IsValid => !string.IsNullOrEmpty(this.Extension) && Filters.Contains(this.Extension);
private IFormFile file;
public IFormFile File
{
get { return file; }
set
{
if (value != null)
{
this.file = value;
this.FileType = this.file.ContentType;
this.Length = this.file.Length;
this.Extension = this.file.FileName.Substring(file.FileName.LastIndexOf('.'));
if (string.IsNullOrEmpty(this.FileName))
this.FileName = this.FileName;
}
}
}
public async Task<string> SaveAs(string destinationDir = null)
{
if (this.file == null)
throw new ArgumentNullException("沒有需要保存的文件");
if (destinationDir != null)
Directory.CreateDirectory(destinationDir);
var newName = DateTime.Now.Ticks;
var newFile = Path.Combine(destinationDir ?? "", $"{newName}{this.Extension}");
using (FileStream fs = new FileStream(newFile, FileMode.CreateNew))
{
await this.file.CopyToAsync(fs);
fs.Flush();
}
return newFile;
}
}
- UserFile 是一個帶保持文件行為的實體類,該類的公共屬性用於從表單域中接收和屬性名稱相同的表單值,其中公共屬性 File 用於接收文件,併在設置值的時候去做一些其它屬性初始化的工作,比如文件長度和擴展名、文件類型
- 其中還實現了一個簡單的文件過濾器,判斷客戶端上傳的文件是否屬於服務端允許上傳的文件擴展名
- 最後 SaveAs(string destinationDir = null) 通過傳入指定目錄,將文件保存,並返回保存後的文件絕對路徑
3. 上傳文件
- 3.1 下麵就定義一個簡單的 API 介面,用於測試上傳文件
[HttpPost]
public async Task<IActionResult> Post([FromFile]UserFile file)
{
if (file == null || !file.IsValid)
return new JsonResult(new { code = 500, message = "不允許上傳的文件類型" });
string newFile = string.Empty;
if (file != null)
newFile = await file.SaveAs("/data/files/images");
return new JsonResult(new { code = 0, message = "成功", url = newFile });
}
3.2 首先是在 Post([FromFile]UserFile file) 中使用上面創建的 FromFileAttribute 對模型 UserFile 進行綁定,然後驗證文件是否正確,接下來通過 file.SaveAs("/data/files/images"); 保存文件
3.3 上傳代碼非常簡單,幾乎到了無法精簡的程度,最終發揮作用的就是 file.SaveAs 操作
4. 上傳測試
- 4.1 現在通過控制台啟動服務
- 4.2 使用 Postman 模擬表單上傳文件
- 4.3 上傳成功,現在來查看目錄下是否有文件
結語
- 在上傳表單中,我們定義了附件的名稱為 file 對應綁定模型的公共屬性 File,這樣模型就可以自動獲得該文件
- 表單中還傳遞了另外一個欄位 filename,對應綁定模型的公共屬性 FileName,實現自定義文件友好顯示名稱
- 通過自定義模型綁定,實現了快速上傳文件功能,該功能只能用於上傳小文件,對於大文件,還是需要實現分片上傳,或者使用 CDN 等服務商的介面
示例代碼下載
(經 海闊天空XM. 提醒,由於上傳了編譯後的 dll 引起部分朋友殺毒軟體誤報,現已重新上傳,感謝!)
https://files.cnblogs.com/files/viter/Ron.UploadFile.zip