表单与文件上传
三种常见正文格式
| 内容格式 | 常见用途 | 声明工具 |
|---|---|---|
application/json | 结构化对象 | BaseModel、Body |
application/x-www-form-urlencoded | 普通表单 | Form |
multipart/form-data | 文件与附加字段 | File、UploadFile、Form |
表单与文件解析需要 python-multipart;标准依赖安装通常会包含它,缺少时可以显式安装。官方表单说明、官方文件上传说明
powershell
python -m pip install python-multipart完整示例:表单与分块读取上传文件
python
from typing import Annotated
from fastapi import FastAPI, File, Form, HTTPException, UploadFile
app = FastAPI()
max_file_size = 2 * 1024 * 1024
@app.post("/form-demo")
def form_demo(
username: Annotated[str, Form(min_length=1)],
note: Annotated[str, Form()] = "",
):
return {"username": username, "note": note}
@app.post("/upload")
async def upload_file(
file: Annotated[UploadFile, File()],
description: Annotated[str, Form()] = "",
):
size = 0
try:
while chunk := await file.read(64 * 1024):
size += len(chunk)
if size > max_file_size:
raise HTTPException(status_code=413, detail="文件不能超过 2 MiB")
return {
"filename": file.filename,
"content_type": file.content_type,
"size": size,
"description": description,
}
finally:
await file.close()while chunk := ... 使用赋值表达式:读取一块并放入 chunk,当结果为空时停止。UploadFile 使用暂存机制,适合比简单 bytes 参数更大的文件;一次性 await file.read() 仍然可能把内容全部读入内存。
本例只读取并统计大小,没有保存文件。这个大小检查发生在表单解析后;真实服务要限制上传过程的资源消耗,还需结合代理或服务入口的请求大小限制。
上传时的常见误区
- 文件名和
content_type来自客户端,不代表内容一定可信。 - 保存文件时,应生成服务器侧文件名,避免直接把用户文件名当磁盘路径。
- 大文件需要分块、容量限制和清理机制。
- 文件和其他字段可以放在同一个 multipart 请求中,但不能同时把同一请求体当作一个独立 JSON 正文解析。
- multipart 中可以放一个内容为 JSON 字符串的表单字段,再显式解析;这与直接声明 JSON
Body是不同方案。
本章练习
上传一个小文本文件,核对返回字节数;上传超过 2 MiB 的文件,确认返回 413。
过关标准:知道何时用模型、Form 或 UploadFile,以及为什么不能随意相信上传文件名。