Skip to content

表单与文件上传 ​

三种常见正文格式 ​

内容格式常见用途声明工具
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,以及为什么不能随意相信上传文件名。