FastAPI表单数据处理:接收multipart/form-data

在Web开发中,我们经常需要处理用户提交的表单数据,尤其是当需要上传文件(如图片、文档)时,通常会用到multipart/form-data格式。这种格式可以同时传输文本数据和二进制文件数据,非常适合表单与文件混合的场景。FastAPI框架提供了简单直观的方式来接收这类数据,让我们一步步学习如何处理。

一、什么是multipart/form-data?

multipart/form-data是HTTP协议中的一种请求格式,用于在表单中包含多种类型的数据。例如,用户上传图片时,同时提交用户名、年龄等文本信息,以及图片文件。这种格式会将数据分割成多个部分(part),每个部分可以是文本或二进制文件。

二、FastAPI处理multipart/form-data的准备

要在FastAPI中接收multipart/form-data,需要导入两个关键工具:
- Form:用于接收表单中的文本数据。
- File(或UploadFile):用于接收文件数据。

其中,FileUploadFile都用于文件上传,但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)时,使用FileUploadFileFile返回文件的二进制内容(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获取文件元数据

UploadFileFile更强大,支持获取文件名、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()  # 关闭文件(避免资源泄漏)

关键点
- UploadFilefile参数是必填的(用...标记)。
- 通过file.filename获取原始文件名,file.content_type获取MIME类型,file.size获取文件大小。
- 使用file.file.read()读取二进制内容,with open(...)保存到磁盘。
- 最后必须调用file.file.close()关闭文件,避免内存泄漏。

五、混合文本与文件的完整示例

如果需要同时接收文本和文件,只需在路由函数中同时使用FormFile(或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 "无文件上传"
    }

注意
UploadFileread()方法是异步的(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接收文本,FileUploadFile接收文件。
  • File适合简单二进制内容,UploadFile适合获取元数据和保存文件。
  • 使用Swagger UI可快速测试接口,无需额外工具。

通过以上示例,你已经可以处理大多数表单与文件混合的场景。如果需要更复杂的文件处理(如分块上传、大文件流式处理),可以进一步探索UploadFile的高级功能(如save_to方法、file对象操作)。

小夜