歡迎光臨
每天分享高質量文章

ASP.NET Core 一行程式碼搞定檔案上傳

前言

在 Web 應用程式開發過程中,總是無法避免涉及到檔案上傳,這次我們來聊一聊怎麼去實現一個簡單方便可復用檔案上傳功能;透過建立自定義系結模型來實現檔案上傳。

一、實現自定義系結模型

1.1、 在 Asp.Net Core MVC 中,內建了很多種系結模型,讓我們可以很方便的去使用,比如下麵常用的幾種系結模型

 

FromBodyAttribute
FromFromAttribute
FromQueryAttribute
FromHeaderAttribute
FromServicesAttribute
FromRouteAttribute

 

常見用法比如

 

[HttpPost]
public async Task PostInfo([FromBody]UserInfo user,[FromQuery] string city)
{
    ...
}

 

檢視以上系結模型,唯獨缺少一個 FromFileAttribute ,下麵就來實現一個自己的 FromFileAttribute

 

public class FromFileAttribute : Attribute, IBindingSourceMetadata
{
    public BindingSource BindingSource => BindingSource.FormFile;
}

 

非常簡單,就三行程式碼,完全照抄系統內建的系結模型,唯一不同的就是指定 BindingSource 為 BindingSource.FormFile。

二、實現一個上傳檔案物體類,專門用於接收客戶端引數

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.1、下麵就定義一個簡單的 API 介面,用於測試上傳檔案

 

[HttpPost]
public async Task 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.1 現在透過控制檯啟動服務

 

 

4.2 使用 Postman 模擬表單上傳檔案

 

 

4.3 上傳成功,現在來檢視目錄下是否有檔案

 

結語

  • 在上傳表單中,我們定義了附件的名稱為 file 對應系結模型的公共屬性 File,這樣模型就可以自動獲得該檔案

  • 表單中還傳遞了另外一個欄位 filename,對應系結模型的公共屬性 FileName,實現自定義檔案友好顯示名稱

  • 透過自定義模型系結,實現了快速上傳檔案功能,該功能只能用於上傳小檔案,對於大檔案,還是需要實現分片上傳,或者使用 CDN 等服務商的介面

 

示例程式碼下載

https://files.cnblogs.com/files/viter/Ron.UploadFile.zi

贊(1)

分享創造快樂