Refit 使用紀錄II - 下載上傳檔案
Refit, HttpClient 應用簡化方案之一。WASM, Blazor WebAssembly 下載檔案、上傳檔案。
開發環境
平台:.NET6
骨架:Blazor WASM App
IDE:Visual Studio 2022
關鍵原碼紀錄-下載檔案-Client
@using System.Net
@page "/file"
@attribute [Authorize]
@inject IFileHandleApi bizApi
@inject JSInterOpService jsTool
<MudToolBar Class="gap-3">
<MudButton Variant=Variant.Filled Color=Color.Primary OnClick=HandleDonloadFile>測試下載檔案</MudButton>
<MudCheckBox @bind-Checked=f_testFail Color=Color.Warning>測試邏輯失敗</MudCheckBox>
</MudToolBar>
[...略...]
@code {
//## State
string errMsg = string.Empty;
bool f_loading = false;
string message = string.Empty;
bool f_testFail = false;
async Task HandleDonloadFile()
{
// 模擬下載失敗狀況
Guid id = f_testFail ? Guid.Empty : Guid.NewGuid();
HttpContent content = await bizApi.DowloadFileAsync(id);
byte[] fileBlob = await content.ReadAsByteArrayAsync();
// 取得檔名
string filenameU = content.Headers.GetValues("Content-Disposition").First().Split("filename*=UTF-8''")[1];
string filename = Uri.UnescapeDataString(filenameU); // 解碼
await jsTool.DownloadFileAsync(filename, fileBlob);
}
}
關鍵原碼紀錄-上傳檔案-Client
上傳檔案用 FormData 封包處理。在 ASP.NET 名為 MultipartFormDataContent。
@using System.Net
@using System.Net.Http.Headers
@using Microsoft.AspNetCore.Components.WebAssembly.Hosting
@page "/fileup"
@attribute [Authorize]
@inject IFileHandleApi bizApi
@inject JSInterOpService jsTool
@inject IWebAssemblyHostEnvironment env
<MudFileUpload T="IReadOnlyList<IBrowserFile>" FilesChanged=HandleUploadFiles>
<ButtonTemplate>
<MudFab HtmlTag="label"
Color="Color.Secondary"
StartIcon=@Icons.Material.Filled.CloudUpload
Label="選取上傳檔案"
for="@context" />
</ButtonTemplate>
</MudFileUpload>
@if (uploadResults.Count > 0)
{
<ul>
@foreach (var item in uploadResults)
{
<li>
Upload file: @item.FileName => @item.StoredFileName
</li>
}
</ul>
}
[...略...]
@code {
//## Resource
const int maxAllowedFiles = int.MaxValue;
const long maxFileSize = long.MaxValue;
//## State
List<string> fileNames = new();
List<UploadResult> uploadResults = new();
string errMsg = string.Empty;
bool f_loading = false;
Task HandleUploadFiles(IReadOnlyList<IBrowserFile> files) => CatchHandling(async () =>
{
using var content = new MultipartFormDataContent();
fileNames.Clear();
foreach (IBrowserFile file in files)
{
var fileContent = new StreamContent(file.OpenReadStream(maxFileSize));
fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType);
fileNames.Add(file.Name);
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
}
var newUploadResults = await bizApi.UploadFileAsync(content);
if (newUploadResults is not null)
{
uploadResults = uploadResults.Concat(newUploadResults).ToList();
}
});
}
若用 HttpClient 上傳檔案,語法如下:
using var content = new MultipartFormDataContent();
foreach (IBrowserFile file in files)
{
var fileContent = new StreamContent(file.OpenReadStream(maxFileSize));
fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType);
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
}
using HttpClient Http = new HttpClient();
Http.BaseAddress = new Uri(env.BaseAddress);
var resp = await Http.PostAsync("api/FileHandle/UploadFile", content);
var newUploadResults = await resp.Content.ReadFromJsonAsync<List<UploadResult>>();
定義下載上傳 Refit Client 介面
using Refit;
using SmallEco.DTO;
namespace SmallEco.Client.RefitClient;
public interface IFileHandleApi
{
[Post("/api/FileHandle/DownloadFile")]
Task<HttpContent> DowloadFileAsync(Guid id);
[Post("/api/FileHandle/UploadFile")]
Task<List<UploadResult>> UploadFileAsync(MultipartFormDataContent content);
}
關鍵原碼紀錄 - Server side
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Net.Mime;
using Swashbuckle.AspNetCore.Annotations;
using SmallEco.DTO;
using SmallEco.Models;
using System.Net;
namespace SmallEco.Server.Controllers;
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class FileHandleController : ControllerBase
{
//# for injection
readonly ILogger<FileHandleController> _logger;
readonly IWebHostEnvironment _env;
public FileHandleController(ILogger<FileHandleController> logger, IWebHostEnvironment env)
{
_logger = logger;
_env = env;
}
[HttpPost("[action]")]
[SwaggerResponse(200, type: typeof(string))]
public IActionResult Echo()
{
return Ok($"echo at {DateTime.Now:HH:mm:ss.}");
}
[HttpPost("[action]")]
[SwaggerResponse(200, type: typeof(byte[]))]
[SwaggerResponse(400, type: typeof(ErrMsg))]
public IActionResult DownloadFile(Guid id)
{
// 模擬邏輯失敗!
if (id == Guid.Empty)
{
return BadRequest(new ErrMsg("模擬邏輯失敗!"));
}
FileInfo fi = new FileInfo(@"Template\這是下載檔案.xlsx");
if (System.IO.File.Exists(fi.FullName))
{
return File(System.IO.File.ReadAllBytes(fi.FullName), "application/octet-stream", fi.Name);
}
return NotFound();
}
[HttpPost("[action]")]
[SwaggerResponse(200, type: typeof(List<UploadResult>))]
[SwaggerResponse(400, type: typeof(ErrMsg))]
public async Task<IActionResult> UploadFile(List<IFormFile> files)
{
//// 模擬邏輯失敗!
//return BadRequest(new ErrMsg("我錯了!"));
List<UploadResult> uploadResults = new();
foreach (IFormFile file in files)
{
string trustedFileNameForDisplay = WebUtility.HtmlEncode(file.FileName);
string trustedFileNameForStorage = Path.GetRandomFileName();
string path = Path.Combine(_env.ContentRootPath, "uploads", trustedFileNameForStorage);
///※ 上傳檔將存入 uploads 目錄裡
await using FileStream fs = new FileStream(path, FileMode.Create);
await file.CopyToAsync(fs);
uploadResults.Add(new UploadResult
{
FileName = trustedFileNameForDisplay,
StoredFileName = trustedFileNameForStorage
});
}
return Ok(uploadResults);
}
}
完整原始碼
參考
Refit 使用紀錄下載
上傳
Last updated