FastAPI 处理 HTML 表单数据 (Form) 和 文件上传 (File/UploadFile) — 与 JSON bodies 不同,因为它们使用不同的内容类型 (multipart/form-data)。两者都使用特殊的参数类型声明。
from fastapi import Form
@app.post("/login")
def login(username: str = Form(), password: str = Form()): # form fields, not JSON
# data comes as application/x-www-form-urlencoded or multipart/form-data
return {"username": username}
使用 Form() 接收 HTML 表单字段(例如登录表单)。注意:一个路由只能使用 JSON body 或 表单数据,不能同时使用,因为它们是不同的内容类型。(需要安装 python-multipart。)
from fastapi import File, UploadFile
@app.post("/upload")
async def upload(file: UploadFile): # a single file
contents = await file.read() # read the file's bytes
file.filename # original name
file.content_type # MIME type
# save it:
with open(f"uploads/{file.filename}", "wb") as f:
f.write(contents)
return {"filename": file.filename, "size": len(contents)}
UploadFile 是推荐的类型 — 它是一个 spooled 文件(大文件存储到磁盘,不是全部在内存中),具有异步方法 (read, write, seek) 和元数据。除了极小的文件外,应该使用它而不是原始 bytes。
@app.post("/upload-many")
async def upload_many(files: list[UploadFile]): # multiple files
return [f.filename for f in files]
@app.post("/profile")
async def profile(name: str = Form(), avatar: UploadFile = File()):
# combine form fields AND a file upload in one multipart request
...
if file.content_type not in ["image/jpeg", "image/png"]: # check type
raise HTTPException(400, "Invalid file type")
# also: limit file SIZE, sanitize filenames, scan content — uploads are untrusted input
处理表单数据和文件上传是常见的、实际的需求 — 登录表单、头像上传、文档提交和文件处理 API 都需要它,而且它与 FastAPI 默认使用的 JSON bodies 的工作方式不同。
理解 Form() 用于表单字段,特别是 UploadFile 用于上传(推荐方法,因为它将大文件 spool 到磁盘而不是将所有内容加载到内存中 — 这对于处理大文件而不耗尽 RAM 是重要的)是必要的。
同样重要的是上传带来的 安全责任:由于上传的文件是不受信任的输入,验证其类型和大小、清理文件名、并谨慎处理如何存储/提供文件对于避免漏洞是必需的。
知道如何接收表单字段、处理单个和多个文件上传、组合表单和文件、以及安全地验证上传内容是构建接受用户提交内容的真实 API 的实用知识。