FastAPI文件上傳:從基礎到進階的完整教程

在現代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)替代本地存儲,避免服務器磁盤壓力過大。

小夜