選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
2 分鐘閱讀時間 (435 個字)

[.Net core] HttpMessageResponse Download File not work

despaired-2261021_1920

問題的發現 

最近使用.Net core 3.1 翻新系統時,遇到了個問題, 原本只是很單純的下個Web API 到後端取檔案再return回前端, .Net Framework 4.5.2 中的寫法是這樣:

[Route("downloadfile")]
[HttpPost()]
public HttpResponseMessage DownloadFile([FromBody] string fileName)
{
    var filePath = "C:\\Template.xlsx";
    HttpResponseMessage result = new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
    result.Content = new StreamContent(new FileStream(filePath, FileMode.Open, FileAccess.Read));
    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
    {
        FileName = Path.GetFileName(filePath)
    };
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-excel");
    return result;
} 

假設我們要從後端取得一個Template.xlsx檔案,返回一個HttpResponseMessage類型,用開發者工具看到response的結果,會收到:

但在.Net core底下用相同的方式return HttpResponseMessage:

(跟.net framwork中一模一樣的寫法)

[Route("downloadfile")]
[HttpPost()]
public HttpResponseMessage DownloadFile([FromBody] string fileName)
{
    var filePath = "C:\\Template.xlsx";
    HttpResponseMessage result = new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
    result.Content = new StreamContent(new FileStream(filePath, FileMode.Open, FileAccess.Read));
    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
    {
        FileName = Path.GetFileName(filePath)
    };
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-excel");
    return result;
} 

response只會顯示一串Json格式的資料,回傳的是序列化的結果:

Content-Length很小,看起來沒有夾帶任何檔案內容:

查了一下官方文件

HttpResponseMessage也適用於.Net core 3.1阿!!!

到底發生什麼事了?!

 原因

.net core的Web API中,把HttpResponseMessage當成一個model來處理,所以使用HttpResponseMessage會是返回Json。參考資料

 解法

1. 使用 WebApiCompatShim

可以參考此說明

但我沒有很喜歡多去增加套件的方法,所以使用的是後面幾種解法。

 2. 使用ActionResult返回File類型

直接把File檔案返回給前端的方式,在.Net core中做起來也是沒有問題的:

[Route("downloadfile")]
[HttpPost()]
public ActionResult DownloadFile([FromBody] string fileName)
{
    var filePath = "C:\\Template.xlsx";
    var bytes = System.IO.File.ReadAllBytes(filePath);
    return File(bytes, "application/vnd.ms-excel", Path.GetFileName(filePath));
} 

考量到後端檔案讀取時間,也可以使用非同步的方式返回。

[Route("downloadfile")]
[HttpPost()]
public async Task<ActionResult> DownloadFile([FromBody] string fileName)
{
    var filePath = "C:\\Template.xlsx";
    var bytes = await System.IO.File.ReadAllBytesAsync(filePath);
    return File(bytes, "application/vnd.ms-excel", Path.GetFileName(filePath));
} 

 3. 若是想要用Http Response回傳的話,就要使用IActionResult類型返回

返回FileStreamResult

[Route("downloadfile")]
[HttpPost()]
public IActionResult DownloadFile([FromBody] string fileName)
{
    var filePath = "C:\\Template.xlsx";
    var result = new FileStream(filePath, FileMode.Open);
    return new FileStreamResult(result, "application/vnd.ms-excel");
} 

返回FileContentResult

若你想夾帶Content,可以返回FileContentResult:

[Route("downloadfile")]
[HttpPost()]
public IActionResult DownloadFile([FromBody] string fileName)
{
    var filePath = "C:\\Template.xlsx";
    var result = new FileContentResult(System.IO.File.ReadAllBytes(filePath), "application/vnd.ms-excel")
    {
        FileDownloadName = Path.GetFileName(filePath)
    };
    return result;
} 

記得需於Startup.cs增加Policy,將Content-Disposition加入回傳的Header中:

//Startup.cs
services.AddCors(options =>
{
    options.AddPolicy("CorsPolicy", policy =>
    {
        policy.WithOrigins("http://localhost:4200")
            .WithExposedHeaders("Content-Disposition");
    });
}); 

以上是翻新系統採到的小雷,希望對大家有幫助,內容有誤的話再請大家多多指教!

如何在 Hangfire上實作自訂工作重試處理
玩轉 Fiddler-HTTP(s) 抓包能手 & 常見「特殊」用途

相關文章

 

評論 1

Guest - 邱柏仁 於 2022/03/29, 週二 16:35

既然都要讀取檔案了,怎麼不PhysicalFile就好了呢?

既然都要讀取檔案了,怎麼不PhysicalFile就好了呢?
已經注冊了? 這裡登入
Guest
2024/04/24, 週三

Captcha 圖像