File Result:傳送 CSV 檔案實體和測試
目錄
- 傳送檔案實體:CSV
- Unit Test
傳送檔案實體
使用者有時候需要下載資料,對我而言最簡單直接的方式是在 server 端存成檔案之後,把連結回傳給前端,讓瀏覽器另開 URL 進行下載。好處是能保存使用者的下載紀錄,壞處是會占用空間,所以想找找看有沒有不需要存成檔案的方法。
傳送實體的方式很簡單:
using System.Net.Mime;
using System.Text;
using Microsoft.AspNetCore.Mvc;
namespace download_file.Controllers;
[ApiController]
[Route("api/[controller]")]
public class DownloadController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> Download()
{
var mediaType = MediaTypeNames.Application.Octet;
var buffer = await CreateContent();
return File(buffer, mediaType);
}
private async Task<byte[]> CreateContent()
{
using var buffer = new MemoryStream();
await using var sw = new StreamWriter(buffer, Encoding.UTF8);
await sw.WriteLineAsync("Key,Value");
await sw.WriteLineAsync("Id,123");
await sw.FlushAsync();
await buffer.FlushAsync();
buffer.Position = 0;
return buffer.ToArray();
}
}簡單的前端頁面接取:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>下載測試</title>
</head>
<body>
<div id="app">
<button type="button" @click.prevent="download">下載</button>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/[email protected]/dist/axios.min.js"></script>
<script>
const app = Vue.createApp({
methods:{
download(){
axios({
method: 'post',
url: './api/download'
}).then(res => {
const url = window.URL
.createObjectURL(new Blob([res.data]));
console.log(res)
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `result.csv`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
alert('下載成功');
})
},
},
});
app.mount('#app');
</script>
</body>
</html>下載成功的 Console.log:

Unit Test
對於最近在練習單元測試的我,這是一個很棒的挑戰:如何幫檔案下載寫一個小小的測試。其中重要也最困難的應該是如何接到檔案,以及取得檔案的內容:
這邊要注意的是第 17 行中,要將 response 轉換成 FileContentResult 的部分,他會根據你原本在 Controller 中所回傳類型而有不同:
using System.Net.Mime;
using System.Text;
using download_file.Controllers;
using Microsoft.AspNetCore.Mvc;
namespace TestProject2;
public class Tests
{
[Test]
public async Task download_file_success()
{
var content = await GivenContentStream();
var downloadController = new DownloadController();
var response = await downloadController.Download();
var result = response as FileContentResult;
Assert.AreEqual(MediaTypeNames.Application.Octet, result.ContentType);
Assert.AreEqual(content, result.FileContents);
}
private async Task<byte[]> GivenContentStream()
{
using var buffer = new MemoryStream();
await using var sw = new StreamWriter(buffer, Encoding.UTF8);
await sw.WriteLineAsync("Key,Value");
await sw.WriteLineAsync("Id,123");
await sw.FlushAsync();
await buffer.FlushAsync();
buffer.Position = 0;
return buffer.ToArray();
}
}new File(byte[], …)要對應FileContentResultnew File(stream, …)要對應FileStreamResult