在FastAPI的实际业务开发中,经常会遇到需要同时上传文件和传递复杂结构化数据的需求,比如批量上传图片的同时提交每张图片的名称、分类、标签等元信息,这些元信息通常以列表字典的形式存在,需要结合Pydantic模型进行校验和解析。本文将详细介绍如何实现这类接口的开发。

核心实现思路
FastAPI中处理文件上传使用File和UploadFile类型,处理结构化数据使用Form或者Body参数,而Pydantic模型可以用来定义列表字典数据的结构。由于文件上传通常使用multipart/form-data格式,因此需要将列表字典数据转换为JSON字符串放在表单字段中,再通过Pydantic模型解析。
定义Pydantic模型
首先定义用于校验列表字典数据的Pydantic模型,这里以文件元信息为例,每个字典包含文件名、分类、标签三个字段:
from pydantic import BaseModel, Field
from typing import List, Dict, Any
# 单个文件元信息模型
class FileMetaItem(BaseModel):
file_name: str = Field(..., description="文件名称")
category: str = Field(..., description="文件分类")
tags: List[str] = Field(default_factory=list, description="文件标签")
# 批量文件元信息模型,对应列表字典结构
class FileMetaList(BaseModel):
meta_list: List[FileMetaItem] = Field(..., description="文件元信息列表")
接口参数定义
接口需要同时接收文件参数和表单中的JSON字符串参数,文件参数使用UploadFile,JSON字符串参数使用Form接收,再手动解析为Pydantic模型:
from fastapi import FastAPI, File, UploadFile, Form, HTTPException
from pydantic import ValidationError
import json
app = FastAPI()
@app.post("/upload-files-with-meta")
async def upload_files_with_meta(
# 接收多个文件,files是文件列表
files: List[UploadFile] = File(...),
# 接收表单中的元信息JSON字符串
meta_json: str = Form(...)
):
# 解析JSON字符串为Pydantic模型
try:
meta_data = json.loads(meta_json)
file_meta = FileMetaList(**meta_data)
except json.JSONDecodeError:
raise HTTPException(status_code=400, detail="元信息JSON格式错误")
except ValidationError as e:
raise HTTPException(status_code=400, detail=f"元信息校验失败: {e}")
# 处理文件和元信息的逻辑
result = []
for file, meta_item in zip(files, file_meta.meta_list):
# 这里可以添加文件保存、数据库存储等逻辑
result.append({
"file_name": file.filename,
"meta": meta_item.dict()
})
return {
"message": "上传成功",
"data": result
}
请求发送示例
使用requests库发送请求的示例如下,需要注意表单数据的格式:
import requests
import json
url = "http://127.0.0.1:8000/upload-files-with-meta"
# 准备文件列表
files = [
("files", open("test1.jpg", "rb")),
("files", open("test2.png", "rb"))
]
# 准备元信息列表字典,转换为JSON字符串
meta_list = [
{"file_name": "test1.jpg", "category": "图片", "tags": ["风景", "旅游"]},
{"file_name": "test2.png", "category": "图片", "tags": ["设计", "素材"]}
]
meta_json = json.dumps({"meta_list": meta_list})
# 发送请求
response = requests.post(
url,
files=files,
data={"meta_json": meta_json}
)
print(response.json())
注意事项
- 文件参数和表单参数需要匹配数量,避免出现文件数量和元信息数量不一致的情况,可以在接口中添加数量校验逻辑。
- JSON字符串需要放在表单字段中,不要直接作为请求体发送,否则会和文件上传的
multipart/form-data格式冲突。 - Pydantic模型的字段定义需要和前端传递的字典结构完全匹配,包括字段名称和数据类型,否则会校验失败。
- 如果列表字典中的字典结构不固定,可以使用
Dict[str, Any]类型定义,但会失去Pydantic的字段校验能力。
常见问题解决
问题1:提示元信息参数缺失
检查前端发送请求时是否在data中正确添加了meta_json字段,字段名称需要和接口定义的Form参数名称一致。
问题2:文件数量和元信息数量不匹配
可以在接口中添加如下校验逻辑:
if len(files) != len(file_meta.meta_list):
raise HTTPException(status_code=400, detail="文件数量和元信息数量不一致")
问题3:中文乱码
确保前端发送请求时编码格式为utf-8,FastAPI默认支持utf-8编码,一般不需要额外处理。