Skip to content

ASP.NET Core Web API 檔案上傳處理方式的實踐探討

TLDR

  • 推薦採用「檔案上傳與業務邏輯分離」的架構:先上傳檔案取得 ID,再將 ID 隨業務資料提交。
  • 避免在同一個 API 中混合處理檔案與業務資料,以維持 API 格式的一致性。
  • 使用 [FromForm] 處理檔案上傳,使用 [FromBody] 處理 JSON 業務資料,兩者應分開設計。
  • 檔案下載建議整合至業務邏輯 Controller,以便實作細粒度的權限控管。
  • 針對未完成業務綁定的暫存檔案,應建立排程任務定期清理,避免空間浪費。

開發模式與資料接收方式

在 ASP.NET Core 中,處理檔案上傳主要有兩種情境:

  • 全端開發:通常使用 HTML 表單 Submit,後端透過 [FromForm] 接收。
  • 前後端分離:通常使用 AJAX 傳遞 JSON,後端透過 [FromBody] 接收。

兩種主要處理方法的技術痛點

什麼情況下會遇到這個問題: 當開發者試圖在同一個 API 中同時處理檔案與複雜業務資料時。

  1. 使用表單 Submit ([FromForm])

    • 痛點:ASP.NET Core 對 [FromBody][FromForm] 使用不同的 ModelBinder 機制。若要針對自訂型別處理,需同時實作 ModelBinderJsonConverter,導致維護成本增加。
    • 影響:前後端協作時,若 API 突然從 JSON 改為 FormData,前端傳輸邏輯需大幅重構。
  2. 使用 JSON 傳遞 Base64 字串 ([FromBody])

    • 痛點:資料傳輸量會增加約 1.33 倍。
    • 影響:在大檔案傳輸時,可能引發效能瓶頸。

推薦方案:檔案上傳與業務資料提交分離設計

為了解決上述衝突,建議將檔案上傳與業務邏輯拆分為兩個獨立步驟。

實作流程

  1. 檔案上傳 API:前端使用 [FromForm] 上傳檔案,後端儲存後回傳檔案 ID。
  2. 業務資料 API:前端在新增或修改業務資料時,僅傳遞檔案 ID。
  3. 資料管理
    • 業務資料主表儲存檔案 ID,或透過關聯表處理一對多關係。
    • 建立排程任務,定期清理一天以上未被啟用的暫存檔案。

檔案資料驗證

若需在驗證階段檢查檔案資訊,可透過依賴注入 (DI) 在 ValidationAttribute 中取得服務:

csharp
protected override ValidationResult IsValid(
    object value, ValidationContext validationContext
) {
    using IServiceScope scope = validationContext.CreateScope();
    IFileService service = scope.ServiceProvider.GetRequiredService<IFileService>();
    // 執行驗證邏輯
}

注意事項

檔案類型驗證(如副檔名)容易被竄改,建議僅作為初步過濾,核心安全檢查應包含檔案簽名識別或內容掃描。


延伸探討:檔案下載實作考量

檔案下載的實作方式取決於權限控管需求:

  • 通用檔案下載 API:實作簡單,但難以針對特定業務場景實施細粒度權限檢查。
  • 業務邏輯 Controller 下載:雖然實作較複雜,但能確保每個檔案下載都經過正確的業務權限驗證。

結論:建議將下載邏輯整合至業務 Controller,並將重複的下載功能封裝為共用服務 (Service),以兼顧權限安全性與程式碼複用性。


異動歷程

    • 初版文件建立。
    • 補上 GitHub 範例專案連結。