在現代Web開發中,文件上傳是常見需求。FastAPI作爲高性能、易用性強的Python Web框架,提供了簡潔高效的文件上傳解決方案。本文將從基礎到進階,詳細講解如何使用FastAPI處理文件上傳,並通過實例幫助你快速掌握核心技能。
一、基礎準備:安裝與配置¶
在開始前,請確保已安裝必要的依賴:
pip install fastapi uvicorn
- FastAPI:核心框架,提供文件上傳相關API。
- Uvicorn:ASGI服務器,用於運行FastAPI應用。
二、基礎文件上傳:單個文件¶
2.1 核心概念¶
FastAPI通過UploadFile類處理文件上傳,它比傳統bytes類型更強大,支持獲取文件名、文件類型、文件大小等元數據。
2.2 示例代碼¶
from fastapi import FastAPI, UploadFile, File
from fastapi.responses import JSONResponse
app = FastAPI(title="文件上傳示例")
@app.post("/upload")
async def upload_single_file(file: UploadFile = File(...)):
"""
上傳單個文件(使用UploadFile)
"""
try:
# 讀取文件內容(適用於小文件)
file_content = await file.read()
# 保存文件到本地(示例:保存到項目目錄下的uploads文件夾)
with open(f"uploads/{file.filename}", "wb") as f:
f.write(file_content)
return {
"status": "success",
"filename": file.filename,
"file_type": file.content_type,
"size": len(file_content)
}
except Exception as e:
return JSONResponse(status_code=500, content={"message": f"上傳失敗: {str(e)}"})
finally:
await file.close() # 確保文件關閉,釋放資源
2.3 代碼解釋¶
UploadFile = File(...):聲明文件參數,...表示必填項(Swagger文檔會自動提示)。await file.read():異步讀取文件內容到內存(小文件推薦,大文件建議流式處理)。file.filename:獲取客戶端上傳的原始文件名。file.content_type:獲取文件MIME類型(如image/png)。file.size:獲取文件大小(字節數,FastAPI 0.90+版本支持)。
2.4 測試接口¶
運行應用:
uvicorn main:app --reload # main.py爲文件名,app爲FastAPI實例
訪問Swagger UI(自動生成):http://localhost:8000/docs,在/upload接口中點擊”Try it out”,選擇本地文件即可測試。
三、進階文件上傳:多文件與複雜場景¶
3.1 多文件上傳¶
FastAPI支持同時上傳多個文件,只需將參數定義爲List[UploadFile]:
@app.post("/upload-multiple")
async def upload_multiple_files(files: list[UploadFile] = File(...)):
"""
上傳多個文件
"""
result = []
for file in files:
file_content = await file.read()
with open(f"uploads/{file.filename}", "wb") as f:
f.write(file_content)
result.append({
"filename": file.filename,
"size": len(file_content)
})
return {"files": result, "count": len(files)}
3.2 上傳時攜帶額外表單數據¶
有時需要上傳文件的同時傳遞其他信息(如用戶ID、描述),可通過Form參數實現:
from fastapi import Form
@app.post("/upload-with-data")
async def upload_with_data(
user_id: int = Form(...), # 額外表單數據
description: str = Form(""), # 可選字段
file: UploadFile = File(...) # 文件
):
file_content = await file.read()
return {
"user_id": user_id,
"description": description,
"filename": file.filename
}
3.3 文件大小與類型驗證¶
FastAPI允許通過條件判斷限制文件大小或類型:
@app.post("/validate-file")
async def validate_file(file: UploadFile = File(...)):
# 限制文件大小:1MB(1024*1024字節)
if file.size > 1024 * 1024:
return JSONResponse(status_code=400, content={"message": "文件大小超過1MB"})
# 限制文件類型:僅允許圖片
if file.content_type not in ["image/jpeg", "image/png"]:
return JSONResponse(status_code=400, content={"message": "僅支持JPG/PNG格式"})
# 其他處理邏輯...
return {"message": "文件驗證通過"}
3.4 大文件流式處理¶
對於大文件(如視頻、壓縮包),直接讀取到內存會導致內存溢出。FastAPI支持流式寫入磁盤:
@app.post("/large-file")
async def large_file(file: UploadFile = File(...)):
try:
# 流式寫入(異步讀取,避免內存佔用)
with open(f"uploads/large_{file.filename}", "wb") as f:
async for chunk in file.file: # 逐塊讀取
f.write(chunk)
return {"message": "大文件上傳成功"}
finally:
await file.close()
四、實用技巧與最佳實踐¶
4.1 文件路徑管理¶
上傳文件時建議使用固定目錄(如uploads/),並確保目錄存在:
import os
def ensure_dir(directory):
if not os.path.exists(directory):
os.makedirs(directory)
ensure_dir("uploads") # 啓動時創建目錄
4.2 自定義文件重命名¶
爲避免文件名衝突(如用戶上傳相同名稱文件),可自定義文件名:
import uuid
def get_unique_filename(original_name):
# 保留擴展名,生成唯一文件名
name, ext = os.path.splitext(original_name)
unique_name = f"{uuid.uuid4()}{ext}"
return unique_name
# 使用示例:
filename = get_unique_filename(file.filename)
with open(f"uploads/{filename}", "wb") as f:
f.write(await file.read())
4.3 錯誤處理¶
通過try-except捕獲異常,提升健壯性:
@app.post("/safe-upload")
async def safe_upload(file: UploadFile = File(...)):
try:
if file.size > 5 * 1024 * 1024: # 5MB限制
raise ValueError("文件過大")
# 處理文件...
return {"status": "success"}
except ValueError as e:
return JSONResponse(status_code=400, content={"error": str(e)})
except Exception as e:
return JSONResponse(status_code=500, content={"error": "服務器錯誤"})
五、總結¶
通過本文,你已掌握FastAPI文件上傳的核心技能:
- 基礎:單個文件上傳、使用UploadFile獲取元數據。
- 進階:多文件上傳、表單數據與文件混合上傳、流式處理大文件。
- 實用技巧:文件驗證、路徑管理、錯誤處理。
FastAPI的文件上傳功能既簡單又強大,結合自動生成的API文檔(/docs),可快速構建穩定可靠的文件上傳服務。
提示:生產環境中建議使用專業存儲服務(如MinIO、S3)替代本地存儲,避免服務器磁盤壓力過大。