在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对象操作)。