.NET和.NET Core Web APi FormData多文件上傳對比

前言

最近因維護.NET和.NET Core項目用到文件上傳功能,雖說也做過,但是沒做過什麼對比,藉此將二者利用Ajax通過FormData上傳文件做一個總結,通過視圖提交表單太簡單,這裏不做闡述,希望對有需要的童鞋能有力所能及的幫助。

.NET Web APi FormData文件上傳

我們將參數和文件都通過FormData來上傳,給出如下HTML代碼

<div class="form-horizontal" style="margin-top:80px;">
    <div class="form-group">
        <label class="control-label col-md-2" for="caption">標題</label>
        <div class="col-md-10">
            <input name="title" id="title" type="text" />
        </div>
    </div>

    <div class="form-group">
        <label class="control-label col-md-2" for="caption">文件</label>
        <div class="col-md-10">
            <input name="file" id="file" multiple type="file" />
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" id="btn" value="提交" class="btn btn-success" />
        </div>
    </div>
</div>

恕我有點強迫症,界面好看點,看着也舒服,接下來則是腳本自然不用多說,利用FormData上傳文件網上一搜遍地都是

$(function () {
    $('#btn').click(function () {

        var data = new FormData();

        var title = $('#title').val();
        data.append("title", title);

        var files = $('#file')[0].files;;
        for (var i = 0; i < files.length; i++) {
            data.append("file", files[i]);
        }
        $.ajax({
            url: '/api/upload/upload',
            type: "post",
            cache: false,
            contentType: false,
            processData: false,
            data: data,
        });
    });
});

不過需要注意的是,對現代大多瀏覽器都都已支持將上述contentType設置爲false後,就是在請求頭中添加multipart/form-data,若是老版本瀏覽器則需要在請求頭中手動添加表單多文件上傳標識,如下

beforeSend: function (request) {
    request.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + data.boundary);
}

前端我們已經搞完,接下來我們回到後臺,.NET Web APi已提供專門讀取FormData數據的APi,如下:

//檢查請求是否包含multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
    throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}

//將文件存儲到App_Data文件夾下
var root = HttpContext.Current.Server.MapPath("~/App_Data");

//實例化MultipartFormDat流
var provider = new MultipartFormDataStreamProvider(root);

// 讀取表單數據
await Request.Content.ReadAsMultipartAsync(provider);

若上傳2個文件,此時上傳App_Data目錄下的文件,如下這般

 若要讀取提交的表單參數,我們如下獲取

//獲取表單參數數據
var formData = provider.FormData;

那麼我們怎麼將上述類似臨時文件數據轉換爲我們上傳的文件數據呢?我們只需將上述文件名轉換我們上傳的文件名或者其他自定義文件名稱即可,如下:

// 獲取文件數據
foreach (MultipartFileData file in provider.FileData)
{
    string fileName = file.Headers.ContentDisposition.FileName;

    if (fileName.StartsWith("\"") && fileName.EndsWith("\""))
    {
        fileName = fileName.Trim('"');
    }
    if (fileName.Contains(@"/") || fileName.Contains(@"\"))
    {
        fileName = Path.GetFileName(fileName);
    }

    //將本地文件轉換爲實際所需文件
    File.Move(file.LocalFileName, Path.Combine(root, fileName));
}

 

完美解決問題,當然除了通過上述流讀取表單相關數據外,.NET Web APi還提供了內存表單流,只是利用此流時,表單參數和文件放置在一起,我們需要通過文件相關參數來做區分,然後分別獲取文件和表單參數,如下:

var provider = new MultipartMemoryStreamProvider();

await Request.Content.ReadAsMultipartAsync(provider);

var formData = new NameValueCollection();

foreach (var httpContent in provider.Contents)
{
    var formFileName = httpContent.Headers.ContentDisposition?.FileName?.Trim('\"');
    var formContentType = httpContent.Headers?.ContentType?.ToString();

    if (!string.IsNullOrEmpty(formFileName) && !string.IsNullOrEmpty(formContentType))
    {
        //文件數據
        using (var fileStream = new FileStream(root, FileMode.Create))
        {
            await httpContent.CopyToAsync(fileStream);
        }
    }
    else
    {
        //表單參數
        var formFieldName = httpContent.Headers.ContentDisposition.Name;

        var formFieldValue = await httpContent.ReadAsStringAsync();

        formData.Add(formFieldName, formFieldValue);
    }
}

.NET Core Web APi FormData文件上傳

HTML和腳本在上述已經提供,這裏我們只需關注APi獲取即可。在.NET Core中沒有專門提供獲取FormData數據的APi,那麼我們是如何獲取的呢?網上找了一大圈大部分是來自廣告網站CSDN,不過這些文章都是轉載的博客園,都是如下這樣獲取

[Route("api/[controller]/[action]")]
[ApiController]
public class UploadController : ControllerBase
{
    public IActionResult Upload()
    {
        var files = Request.Form.Files;

        return Ok();
    }
}

如上也沒問題,我能說你這思路還停留在.NET Web APi嗎,啥年代了,還通過請求上下文去獲取,.NET Core靈活綁定機制使用起來它不香嗎,通過如下直接綁定豈不完事

此時有的童鞋又有疑問了,上傳不僅僅包括文件還包括參數,比如上述還有標題,那該如何是好,啊,這個問題,.NET Core的強類型綁定機制它不香嗎,如下定義強類型:

public class ExampleUpload
{
    public string Title { get; set; }
    public List<IFormFile> Files { get; set; }
}

注意:綁定參數時一定要使用[FromForm],否則將出現請求415,同時也要將前端Ajax FormData文件的參數名和強類型參數名一致。

[Route("api/[controller]/[action]")]
[ApiController]
public class UploadController : ControllerBase
{
    public IActionResult Upload([FromForm]ExampleUpload example)
    {
        return Ok();
    }
}

 

總結

.NET Core用起來就是這麼流暢和舒適,它也是真的香啊,好了,關於此二者多文件上傳暫且總結於此,我們下節再會。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章