FastAPI 檔案上傳 (Request Files)
FastAPI 處理檔案上傳非常簡單且強大。它基於 multipart/form-data 編碼。
同樣地,你需要先安裝
python-multipart。
pip install python-multipart使用 File
你可以使用 bytes 來讀取檔案內容。這適合小檔案,因為整個檔案內容會被讀入記憶體。
from typing import Annotated
from fastapi import FastAPI, File
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}
但通常我們更推薦使用 UploadFile。
使用 UploadFile (推薦)
UploadFile 有以下優點:
- 使用 SpooledTemporaryFile:檔案會暫存在記憶體中,直到超過大小限制 (預設 1MB) 才會寫入磁碟。這意味著它可以處理大檔案而不會吃光記憶體。
- 檔案資訊:你可以取得檔名 (
filename)、Content-Type (content_type) 等資訊。 - 非同步介面:它提供
read(),write(),seek(),close()等類似 Python 檔案操作的async方法。
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename, "content_type": file.content_type}
UploadFile 本身不需要像 File(...) 那樣宣告預設值 (雖然你可以寫 file: UploadFile = File(...) 來添加額外驗證),直接寫型別 UploadFile,FastAPI 就知道這是要接收檔案。讀取與儲存檔案
這是一個將上傳的檔案存到伺服器的範例:
import shutil
from pathlib import Path
@app.post("/save-file/")
async def save_file(file: UploadFile):
# 定義儲存路徑
destination_file_path = Path("uploaded_files") / file.filename
# 確保目錄存在
destination_file_path.parent.mkdir(parents=True, exist_ok=True)
# 將上傳的檔案內容寫入目的檔案
with open(destination_file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
return {"filename": file.filename, "saved_at": str(destination_file_path)}
選填檔案上傳 (Optional File Upload)
如果你想讓檔案上傳變成選填的,只需將型別設為 Optional (或型別聯集 | None) 並給預設值 None。
from typing import Optional
@app.post("/optional-upload/")
async def update_profile(file: Optional[UploadFile] = None):
if not file:
return {"message": "No file sent"}
return {"filename": file.filename}
多個檔案上傳
要接收多個檔案,可以使用 List[UploadFile]。
from typing import Annotated
@app.post("/upload-multiple/")
async def upload_multiple_files(files: Annotated[list[UploadFile], File()]):
return {"filenames": [file.filename for file in files]}
HTML 表單範例:
<form action="/upload-multiple/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple />
<input type="submit" />
</form>
File + Form 混用
你可以同時接收檔案 (File) 和表單欄位 (Form)。
from fastapi import File, Form, UploadFile
@app.post("/files-and-form/")
async def handle_file_and_data(
file: UploadFile,
notes: str = Form(...)
):
return {
"filename": file.filename,
"notes": notes,
"file_content_type": file.content_type
}
這在實務上非常常見,例如「上傳頭像並更新使用者名稱」。
總結
- 使用
File讀取小檔案 (bytes)。 - 使用
UploadFile處理一般檔案與大檔案 (推薦)。 - 支援多檔案上傳 (
List[UploadFile])。 - 可以與
Form資料同時使用。