本文 AI 產出,尚未審核

FastAPI – 表單與檔案上傳(Form & File Upload)

主題:回傳檔案(FileResponse)


簡介

在 Web 開發中,檔案的上傳與下載是最常見的需求之一。FastAPI 除了提供簡潔的表單與檔案接收方式外,還內建了 FileResponse,讓我們可以輕鬆地把伺服器上的檔案回傳給用戶端,支援 內容快取、斷點續傳、正確的 MIME type 等功能。

對於需要提供報表、圖片、PDF、或是使用者自行上傳後再下載的系統,掌握 FileResponse 的使用方式是必備技能。本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶你完成 安全、效能佳的檔案回傳


核心概念

1. 為什麼使用 FileResponse 而不是自行讀檔回傳 Response

  • FileResponse 會自動設定 Content-TypeContent-Disposition,讓瀏覽器正確辨識檔案類型與下載行為。
  • 它支援 range 請求(斷點續傳),適合大檔案的下載需求。
  • 內部使用 aiofiles 以非同步方式讀檔,減少阻塞,提高效能。

2. 基本使用方式

from fastapi import FastAPI
from fastapi.responses import FileResponse
import os

app = FastAPI()

@app.get("/download/{filename}")
async def download_file(filename: str):
    # 設定檔案的完整路徑
    file_path = os.path.join("files", filename)
    # 若檔案不存在,拋出 404
    if not os.path.isfile(file_path):
        raise HTTPException(status_code=404, detail="File not found")
    # 回傳檔案,讓瀏覽器直接下載
    return FileResponse(
        path=file_path,
        media_type="application/octet-stream",
        filename=filename   # 下載時的檔名
    )

重點FileResponse 只需要傳入檔案路徑,其他資訊交給它自行處理。

3. 設定 MIME type

若要讓瀏覽器直接預覽(例如圖片、PDF),可以指定正確的 media_type

return FileResponse(
    path=file_path,
    media_type="image/png",   # 讓瀏覽器直接顯示圖片
    filename=filename
)

FastAPI 會根據副檔名自動推斷 media_type,但手動指定可以避免推斷錯誤。

4. 搭配表單上傳後再提供下載

以下示範 上傳檔案、儲存至伺服器,並在上傳成功後回傳檔案的下載連結:

from fastapi import FastAPI, File, UploadFile, Form
from fastapi.responses import JSONResponse
import shutil
import uuid
import os

app = FastAPI()
UPLOAD_DIR = "uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)

@app.post("/upload")
async def upload_file(
    description: str = Form(...),
    file: UploadFile = File(...)
):
    # 產生唯一檔名避免衝突
    ext = os.path.splitext(file.filename)[1]
    unique_name = f"{uuid.uuid4().hex}{ext}"
    file_path = os.path.join(UPLOAD_DIR, unique_name)

    # 非同步寫入檔案
    with open(file_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)

    download_url = f"/download/{unique_name}"
    return JSONResponse(
        content={"msg": "上傳成功", "description": description, "download_url": download_url}
    )

註解

  • UploadFile 內建的 fileSpooledTemporaryFile,適合大檔案。
  • 使用 uuid 保證檔名唯一,避免惡意覆寫。

5. 支援斷點續傳(Range Request)

FileResponse 已內建對 Range 標頭的支援,客戶端(如瀏覽器或下載工具)會自動發送 Range: bytes=0-,FastAPI 會回傳 206 Partial Content。開發者只需要確保檔案路徑正確即可。

@app.get("/video/{video_name}")
async def stream_video(video_name: str):
    video_path = os.path.join("videos", video_name)
    if not os.path.isfile(video_path):
        raise HTTPException(status_code=404, detail="Video not found")
    return FileResponse(video_path, media_type="video/mp4")

常見陷阱與最佳實踐

陷阱 說明 解決方式
檔案路徑硬編碼 直接寫死路徑容易在不同環境(dev / prod)出錯。 使用環境變數或 Pathlib 產生相對路徑。
未驗證檔名 直接使用使用者提供的檔名會產生路徑穿越(../../)風險。 只接受白名單副檔名,或使用 uuid 產生安全檔名。
同步 I/O 使用 open(..., "rb") 讀檔會阻塞事件迴圈。 交給 FileResponse(已使用非同步)或自行使用 aiofiles
忘記設定 Content-Disposition 瀏覽器預設會直接顯示(例如圖片),但有時需要強制下載。 FileResponse 中傳入 filename=,FastAPI 會自動加上 attachment; filename=
大檔案未設定適當緩衝 若一次性讀取整個檔案會佔用過多記憶體。 交給 FileResponse 或自行實作 Chunked Streaming

最佳實踐要點

  1. 使用 Path(pathlib)管理路徑
    from pathlib import Path
    BASE_DIR = Path(__file__).parent
    UPLOAD_DIR = BASE_DIR / "uploads"
    
  2. 限制檔案大小:在 UploadFile 接收前,使用 Middleware 或自訂依賴檢查 Content-Length
  3. 設定快取標頭(若檔案不常變更):
    return FileResponse(
        path=file_path,
        media_type="application/pdf",
        headers={"Cache-Control": "public, max-age=86400"}
    )
    
  4. 日誌與例外處理:記錄檔案下載成功/失敗,並返回統一的錯誤訊息。

實際應用場景

場景 需求 FileResponse 的角色
線上報表系統 使用者點擊「匯出 Excel」產生報表,需即時下載。 產生臨時 Excel 檔後,用 FileResponse 回傳,設定 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
影音串流平台 影片播放需要支援斷點續傳與快取。 直接回傳 MP4 檔,FileResponse 自動處理 Range,讓前端播放器順暢播放。
文件管理系統 使用者上傳 PDF 後,點擊檔名即在新分頁預覽。 設定 media_type="application/pdf",瀏覽器會直接顯示 PDF。
教育平台 老師上傳課程教材(ZIP),學生下載。 使用 filename= 讓下載時保留原檔名,並加上 Cache-Control 以減少重複下載。
API 下載服務 第三方系統以程式方式取得 CSV 檔。 回傳 Content-Type: text/csv,並可在 Header 中加入 Content-Disposition: attachment; filename="data.csv"

總結

  • FileResponse 是 FastAPI 提供的高效檔案回傳工具,自動處理 MIME、快取、斷點續傳等細節。
  • 使用時只要確保 檔案路徑安全、正確設定 media_typefilename,即可讓瀏覽器或客戶端得到預期的下載或預覽行為。
  • 配合 表單上傳UUID 命名Pathlib 等最佳實踐,能夠建構出 安全、可維護且效能佳 的檔案服務。

掌握了以上概念與範例後,你就能在 FastAPI 專案中輕鬆實作各式檔案上傳、儲存與回傳的功能,為使用者提供流暢且可靠的檔案體驗。祝開發順利!