1. 程式人生 > >Asp.Net Core 2.1 WebAPI 通過IFormFileCollection實現多圖片上傳

Asp.Net Core 2.1 WebAPI 通過IFormFileCollection實現多圖片上傳

背景

最近在學習 ASP.Net Core 2.1 WebAPI, 在做一個基於 Xamarin.Forms 3.1 App和 WebAPI 後端的小專案,其中有個功能要從手機端選擇多張圖片並且上傳到部署在Azure App Service上的WebAPI並把圖片儲存在wwwroot的Photos資料夾下。由於官方文件沒有關於WebAPI檔案上傳的章節,因此參考了MVC版的File Uploads,用List<IFormFile>來做多圖片上傳的接收引數型別,碰到各種的問題,搜遍很多文章都沒有找到滿意的答案。後來反覆檢視官方文件關於ApiController的一篇文章中提到IFormFileCollection,才把上傳功能解決。

第一次寫部落格,希望此文章能為各位熱愛著.Net,熱愛著ASP.Net Core的新手看官帶來幫助~~

關於官方教程

目前來說沒有看到關於ASP.Net Core 2.1 WebAPI 版本的專門關於File Uploads的教程,唯一可以找到的是MVC版的File Uploads。我們可以看到裡面提到:

When uploading files using model binding and the IFormFile interface, the action method can accept either a single IFormFile or an IEnumerable<IFormFile>

(or List<IFormFile>) representing several files. …

於是照著教程寫了第一個版本的API以及一個用於測試檔案上傳的的MVC頁面,具體見下文。

上傳測試頁面

<h1>Upload Files</h1>
<div class="row">
    <section>
        <form method="post" enctype="multipart/form-data" 
                            action="http://localhost:5000/api/Plants/Photos"
>
<div class="form-group"> <div class="col-md-10"> <p>Upload one or more files using this form:</p> <input type="file" name="files" multiple /> </div> </div> <div class="form-group"> <div class="col-md-10"> <input type="submit" value="Upload" /> </div> </div> </form> </section> </div>

上傳頁面

第一個版本(基於List < IFormFile > )

注意:這個版本無法工作


[Route("api/[controller]")]
[ApiController]
public class PlantsController:ControllerBase
{
    //其他部分省略
    ... 

    [HttpPost("Photos")]
    public async Task<IActionResult> UploadPhotosAsync(List<IFormFile> files)
    {
        long size = files.Sum(f => f.Length);
        var fileFolder = Path.Combine(_env.WebRootPath, "Photos");

        if (!Directory.Exists(fileFolder))
              Directory.CreateDirectory(fileFolder);

        foreach (var formFile in files)
        {
            if (formFile.Length > 0)
            {
                var fileName = DateTime.Now.ToString("yyyyMMddHHmmss")+
                               Path.GetExtension(file.FileName);
                var filePath = Path.Combine(fileFolder, fileName);

                using (var stream = new FileStream(filePath, FileMode.Create))
                {
                    await formFile.CopyToAsync(stream);
                }
            }
        }
        return Ok(new { count = files.Count, size});
    }
}

設定斷點測試

這裡寫圖片描述

測試結果

點選上傳按鈕後發現,直接返回 {“”:[“The input was not valid.”]} , 沒有進入斷點。 Visual Studio 輸出:
VS輸出SerializablleError
VS輸出400錯誤

沒有進入斷點是因為2.1 ApiControllerAttribute的新特性,也就是Validation失敗時自動返回HTTP 400錯誤,因此方法裡面不再需要一以下程式碼:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

將SuppressModelStateInvalidFilter設定為true後可以禁用自動HTTP 400的行為:

services.AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
});

另外ApiControllerAttribute還有自動推斷方法引數值位置的功能,也就是說無需在List<IFormFile>引數前寫[FromForm]來告訴Controller在請求正文的表單資料裡獲取圖片資料。那為什麼Validation失敗呢?(這裡暫時還沒有弄明白,希望各位大佬幫忙解答一下~)

再看看ApiController的官方文件,發現其中有一句這樣描述:
推斷描述
也就是說ApiControllerAttribute會自動推斷IFormFile和IFormFileCollection資料型別為表單檔案。於是抱著試一試的心態改了第二個版本。

第二個版本(基於IFormFileCollection)

把原來的List<IFormFile> 改為IFormFileCollection

[HttpPost("Photos")]
public async Task<IActionResult> UploadPhotosAsync(IFormFileCollection files)
{
      long size = files.Sum(f => f.Length);
      var fileFolder = Path.Combine(_env.WebRootPath, "Photos");

      if (!Directory.Exists(fileFolder))
          Directory.CreateDirectory(fileFolder);

      foreach(var file in files)
      {
           if(file.Length > 0)
           {
                var fileName = DateTime.Now.ToString("yyyyMMddHHmmss")+
                               Path.GetExtension(file.FileName);
                var filePath = Path.Combine(fileFolder, fileName);

                using (var stream = new FileStream(filePath, FileMode.Create))
                {
                      await file.CopyToAsync(stream);
                }
            }
       }

      return Ok(new { count = files.Count, size });
}

測試結果

{“count”:2,”size”:110300} 上傳成功!

總結

由於之前很少接觸ASP.Net為了這個多圖片上傳功能,斷斷續續弄了差不多一個星期終於解決了。感覺就是微軟的教程對新人不是很有好,目前ASP.Net Core 2.1 WebAPI部分剛好沒有這方面的內容(還沒有看全,目錄標題找不到相關內容),所以還是碰了不少坑。網上看到的文章幾乎都是用IEnumerable<IFormFile>,ICollection<IFormFile>,List<IFormFile>等來實現的,我都除錯不過。取消ApiControllerAttribute標識,用[FromForm]List<IFormFile>也接收不到檔案內容(null)。不知道還有其他什麼方法來實現多圖上傳功能?希望瞭解這方面的童鞋們來指點或者交流一下。