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 輸出:
沒有進入斷點是因為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)。不知道還有其他什麼方法來實現多圖上傳功能?希望瞭解這方面的童鞋們來指點或者交流一下。