在Web開發中,我們經常需要處理用戶提交的表單數據,尤其是當需要上傳文件(如圖片、文檔)時,通常會用到multipart/form-data格式。這種格式可以同時傳輸文本數據和二進制文件數據,非常適合表單與文件混合的場景。FastAPI框架提供了簡單直觀的方式來接收這類數據,讓我們一步步學習如何處理。
一、什麼是multipart/form-data?¶
multipart/form-data是HTTP協議中的一種請求格式,用於在表單中包含多種類型的數據。例如,用戶上傳圖片時,同時提交用戶名、年齡等文本信息,以及圖片文件。這種格式會將數據分割成多個部分(part),每個部分可以是文本或二進制文件。
二、FastAPI處理multipart/form-data的準備¶
要在FastAPI中接收multipart/form-data,需要導入兩個關鍵工具:
- Form:用於接收表單中的文本數據。
- File(或UploadFile):用於接收文件數據。
其中,File和UploadFile都用於文件上傳,但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)時,使用File或UploadFile。File返回文件的二進制內容(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獲取文件元數據¶
UploadFile比File更強大,支持獲取文件名、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() # 關閉文件(避免資源泄漏)
關鍵點:
- UploadFile的file參數是必填的(用...標記)。
- 通過file.filename獲取原始文件名,file.content_type獲取MIME類型,file.size獲取文件大小。
- 使用file.file.read()讀取二進制內容,with open(...)保存到磁盤。
- 最後必須調用file.file.close()關閉文件,避免內存泄漏。
五、混合文本與文件的完整示例¶
如果需要同時接收文本和文件,只需在路由函數中同時使用Form和File(或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 "無文件上傳"
}
注意:
UploadFile的read()方法是異步的(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接收文本,File或UploadFile接收文件。 File適合簡單二進制內容,UploadFile適合獲取元數據和保存文件。- 使用Swagger UI可快速測試接口,無需額外工具。
通過以上示例,你已經可以處理大多數表單與文件混合的場景。如果需要更復雜的文件處理(如分塊上傳、大文件流式處理),可以進一步探索UploadFile的高級功能(如save_to方法、file對象操作)。