FastAPI表單數據處理:接收multipart/form-data

在Web開發中,我們經常需要處理用戶提交的表單數據,尤其是當需要上傳文件(如圖片、文檔)時,通常會用到multipart/form-data格式。這種格式可以同時傳輸文本數據和二進制文件數據,非常適合表單與文件混合的場景。FastAPI框架提供了簡單直觀的方式來接收這類數據,讓我們一步步學習如何處理。

一、什麼是multipart/form-data?

multipart/form-data是HTTP協議中的一種請求格式,用於在表單中包含多種類型的數據。例如,用戶上傳圖片時,同時提交用戶名、年齡等文本信息,以及圖片文件。這種格式會將數據分割成多個部分(part),每個部分可以是文本或二進制文件。

二、FastAPI處理multipart/form-data的準備

要在FastAPI中接收multipart/form-data,需要導入兩個關鍵工具:
- Form:用於接收表單中的文本數據。
- File(或UploadFile):用於接收文件數據。

其中,FileUploadFile都用於文件上傳,但UploadFile功能更強大(如獲取文件元數據、保存到磁盤等),我們會在後續示例中逐步介紹。

三、處理文本表單數據

如果只需要接收文本類型的表單數據(如姓名、年齡),可以直接使用Form參數。Form的使用方式與Query類似,但專門用於multipart/form-data格式的文本字段。

示例代碼:

from fastapi import FastAPI, Form

app = FastAPI()

@app.post("/submit-info")
def submit_info(
    name: str = Form(...) ,  # 必填參數,...表示必須提供
    age: int = Form(...)     # 必填整數參數
):
    return {
        "message": "表單數據接收成功!",
        "name": name,
        "age": age
    }

關鍵點
- 使用Form(...)標記參數爲表單字段,...表示該參數是必填的(如果不填,FastAPI會報錯)。
- 如果參數可選,可以設置默認值,例如:age: int = Form(default=18)
- 提交請求時,FastAPI會自動解析multipart/form-data中的文本字段。

四、處理文件上傳

當需要上傳文件(如圖片、PDF)時,使用FileUploadFileFile返回文件的二進制內容(bytes),適合簡單場景;UploadFile返回文件對象,可獲取文件名、大小、保存路徑等元數據。

1. 使用File接收二進制文件

如果只需獲取文件的二進制內容(如直接處理圖片數據),可以用File: bytes

from fastapi import FastAPI, File, Form

app = FastAPI()

@app.post("/upload-text-file")
def upload_text_file(
    name: str = Form(...),
    file: bytes = File(None)  # 可選文件,默認None
):
    return {
        "name": name,
        "file_size": len(file) if file else 0,  # 文件大小(字節)
        "file_content": f"文件內容(前10字節):{file[:10]}" if file else "無文件"
    }

測試方式
啓動FastAPI服務後,訪問http://localhost:8000/docs(Swagger UI),找到/upload-text-file接口,填寫name,點擊“選擇文件”上傳,提交後即可看到文件大小和內容。

2. 使用UploadFile獲取文件元數據

UploadFileFile更強大,支持獲取文件名、MIME類型、文件大小等信息,甚至可以保存文件到磁盤。

from fastapi import FastAPI, UploadFile, File, Form

app = FastAPI()

@app.post("/upload-meta-file")
def upload_meta_file(
    name: str = Form(...),
    file: UploadFile = File(...)  # 必填文件參數
):
    # 獲取文件元數據
    file_info = {
        "filename": file.filename,
        "content_type": file.content_type,  # 文件類型(如image/png)
        "size": file.size,                  # 文件大小(字節)
        "file_type": file.content_type.split("/")[0]  # 提取類型(如image)
    }

    # 保存文件到本地(示例:保存到當前目錄)
    try:
        contents = file.file.read()  # 讀取文件內容
        with open(f"uploaded_{file.filename}", "wb") as f:
            f.write(contents)
        return {
            "message": "文件上傳成功!",
            "file_info": file_info,
            "saved_path": f"uploaded_{file.filename}"
        }
    except Exception as e:
        return {"error": f"文件保存失敗:{str(e)}"}
    finally:
        file.file.close()  # 關閉文件(避免資源泄漏)

關鍵點
- UploadFilefile參數是必填的(用...標記)。
- 通過file.filename獲取原始文件名,file.content_type獲取MIME類型,file.size獲取文件大小。
- 使用file.file.read()讀取二進制內容,with open(...)保存到磁盤。
- 最後必須調用file.file.close()關閉文件,避免內存泄漏。

五、混合文本與文件的完整示例

如果需要同時接收文本和文件,只需在路由函數中同時使用FormFile(或UploadFile)參數:

from fastapi import FastAPI, File, Form, UploadFile

app = FastAPI()

@app.post("/user-info-upload")
def user_info_upload(
    username: str = Form(...),       # 文本參數:用戶名
    age: int = Form(default=18),     # 可選文本參數:年齡(默認18)
    avatar: UploadFile = File(...)   # 可選文件參數:頭像
):
    # 處理文本數據
    user_data = {"username": username, "age": age}

    # 處理文件數據
    file_data = {}
    if avatar:
        file_data = {
            "filename": avatar.filename,
            "content_type": avatar.content_type,
            "size": avatar.size
        }
        # 保存文件到本地
        with open(f"avatar_{avatar.filename}", "wb") as f:
            f.write(await avatar.read())  # 異步讀取(注意:如果用同步函數,需用avatar.file.read())

    return {
        "user_info": user_data,
        "file_info": file_data if file_data else "無文件上傳"
    }

注意
UploadFileread()方法是異步的(async),如果在同步函數中使用,需用await,但FastAPI路由函數默認是同步的,因此可以用avatar.file.read()file是底層文件對象,支持同步讀取)。

六、測試接口

FastAPI自帶Swagger UI(訪問http://localhost:8000/docs),可直接測試接口:
1. 進入/user-info-upload接口頁面。
2. 填寫username(必填)和age(可選,默認18)。
3. 點擊“選擇文件”上傳圖片或文檔。
4. 點擊“Execute”發送請求,查看返回結果。

七、總結

  • multipart/form-data用於混合文本和文件的表單提交。
  • FastAPI通過Form接收文本,FileUploadFile接收文件。
  • File適合簡單二進制內容,UploadFile適合獲取元數據和保存文件。
  • 使用Swagger UI可快速測試接口,無需額外工具。

通過以上示例,你已經可以處理大多數表單與文件混合的場景。如果需要更復雜的文件處理(如分塊上傳、大文件流式處理),可以進一步探索UploadFile的高級功能(如save_to方法、file對象操作)。

小夜