天天看點

ASP.NET CORE WEBAPI檔案下載下傳

ASP.NET CORE WEBAPI檔案下載下傳

最近要使用ASP.NET CORE WEBAPI用來下載下傳檔案,使用的.NET CORE 3.1。考慮如下場景:

檔案是程式生成的。

檔案應該能相容各種格式。

浏覽器可以感覺進行下載下傳。

準備

經過簡單的調研,得到以下結論。

ASP.NET CORE 提供FileResult這種類型的ActionResult,可以直接傳回檔案結果,不需要直接處理HttpResponse。

通過Stream可以直接傳回檔案流供浏覽器下載下傳。

FileStreamResult是FileResult的具體實作,傳回值應該是此類對象。

Stream有多種類型,适合直接記憶體中生成檔案對象的是MemoryStream。

對目标有了基礎的了解,就可以開始動手實作了。

實作

建立好ASP.NET CORE WEBAPI工程,把生成檔案的代碼獨立出來一個函數。我這裡需要是下載下傳一個CSV格式的檔案,是以生成一個CSV檔案。

對于磁盤上的檔案,可以使用FileStream對象,由于我這裡需要運作中生成這個檔案,需要使用MemoryStream。

using var stream = new MemoryStream();

using var writer = new StreamWriter(stream);

//生成标題

var propCollection = ttype.GetProperties();

foreach (var n in propCollection)

{

writer.Write(n.Name);
writer.Write(",");           

}

writer.WriteLine();

//生成内容

foreach (var item in res)

foreach (var n in propCollection)
{
    writer.Write(Convert.ToString(n.GetValue(item)));
    writer.Write(",");
}
writer.WriteLine();           

請不要考慮裡面反射的相關内容,按照自己的邏輯生成CSV即可,我隻是懶得改代碼而已。

代碼中使用到了一些新的文法特性,請注意對低版本的.NET不一定适用。

直接傳回Stream對象給Controller處理,處理代碼如下:

var res = await info.GetAllQueryResult();

var actionresult = new FileStreamResult(res, new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("text/csv"));

return actionresult;

CSV的Content-Type是text/csv,如果下載下傳别的檔案,請自行查詢MIME格式。

調試

直接執行上面的代碼,直接報錯“無法讀取已經關閉的流”。猜測是離開using語句塊的時候,stream自動被關閉了。改動很簡單,去掉using語句,不再報相同錯誤。

但是傳回的檔案長度一直是0,單步調試發現Writer執行完畢之後,stream傳回的長度是0,内容實際上并沒有寫入,想起有一個Flush(),可以添加以確定資料寫入。

單步顯示stream長度有了,但是傳回的長度還是0。繼續單步調試發現Stream的Postion是停在檔案結尾的,這個和直接開始讀取檔案完全不一樣,檔案讀取一般是從開頭開始的,于是直接設定Postion為0,問題解決。

下載下傳能夠成功了,但是檔案名一直顯示的是随機生成的,體驗很差。設定一下FileDownloadName即可。

核心代碼如下:

public async Task GetAllQueryResult()

var stream = new MemoryStream();
var writer = new StreamWriter(stream);
//生成标題
var propCollection = ttype.GetProperties();
foreach (var n in propCollection)
{
    writer.Write(n.Name);
    writer.Write(",");
}
writer.WriteLine();
//生成内容
foreach (var item in res)
{
    foreach (var n in propCollection)
    {
        writer.Write(Convert.ToString(n.GetValue(item)));
        writer.Write(",");
    }
    writer.WriteLine();
}
writer.Flush();
stream.Position = 0;
return stream;           

[HttpPost("file")]

[ProducesResponseType(typeof(FileResult), Status200OK)]

public async Task Download()

var info = new Info();
var res = await info.GetAllQueryResult();

var actionresult = new FileStreamResult(res, new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("text/csv"));
actionresult.FileDownloadName = "Carinfos.csv";
//Response.ContentLength = res.Length;
return actionresult;           

使用swagger調用,最後效果:

總結

後來查了一些資料,總結了一下:

MemoryStream如果使用using語句,會在離開代碼塊的時候自動關閉,實際上ASP.NET CORE會自動處理關閉的事項,不需要使用using語句。

由于生成檔案的過程是從檔案流的開頭一直進行到末尾的,是以向請求端傳回結果時,應當重置Stream的遊标,從0開始傳輸。

記得在使用writer之後使用Flush()以確定資料有寫入。

如果不确定檔案格式,可以直接傳回MIME值為application/oct-stream。

設定FileStreamResult的FileDownloadName屬性可以修改檔案的預設名稱。

(可選)可以通過設定Response.ContentLength來設定檔案的長度。

參考資料:

https://darchuk.net/2019/05/31/asp-net-core-web-api-returning-a-filestream/

原文位址

https://www.cnblogs.com/podolski/p/12682978.html

繼續閱讀