请求文件¶
您可以使用 File 定义由客户端上传的文件。
信息
要接收上传的文件,请先安装 python-multipart。
确保你创建一个虚拟环境,激活它,然后安装它,例如:
$ pip install python-multipart
这是因为上传的文件是以“表单数据”形式发送的。
导入 File¶
从 fastapi 导入 File 和 UploadFile
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File()):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
定义 File 参数¶
以与 Body 或 Form 相同的方式创建文件参数
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File()):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
信息
File 是一个直接继承自 Form 的类。
但请记住,当您从 fastapi 导入 Query、Path、File 等时,它们实际上是返回特殊类的函数。
提示
要声明文件体,您需要使用 File,否则参数将被解释为查询参数或体(JSON)参数。
文件将作为“表单数据”上传。
如果将您的路径操作函数参数的类型声明为 bytes,FastAPI 将为您读取文件,您将以 bytes 的形式接收内容。
请注意,这意味着整个内容将存储在内存中。这对于小文件来说效果很好。
但是,在某些情况下,您可能会受益于使用 UploadFile。
带有 UploadFile 的文件参数¶
将文件参数的类型定义为 UploadFile
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File()):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
与 bytes 相比,使用 UploadFile 有几个优点
- 您不必在参数的默认值中使用
File()。 - 它使用“spooled”文件
- 文件存储在内存中,直到达到最大大小限制,超过限制后将存储在磁盘上。
- 这意味着它适用于图像、视频、大型二进制文件等大型文件,而不会消耗所有内存。
- 您可以获取上传文件的元数据。
- 它具有一个类似文件的
async接口。 - 它公开了一个实际的 Python
SpooledTemporaryFile对象,您可以直接将其传递给期望类似文件对象的其他库。
UploadFile¶
UploadFile 具有以下属性
filename:一个str,包含上传的原始文件名(例如myimage.jpg)。content_type:一个str,包含内容类型(MIME 类型/媒体类型)(例如image/jpeg)。file:一个SpooledTemporaryFile(一个类似文件的对象)。这是实际的 Python 文件对象,您可以直接将其传递给期望“类似文件”对象的其他函数或库。
UploadFile 具有以下 async 方法。它们都调用底层的相应文件方法(使用内部的 SpooledTemporaryFile)。
write(data):将data(str或bytes)写入文件。read(size):读取文件中的size(int)字节/字符。seek(offset):将文件指针移动到offset(int)字节位置。- 例如,
await myfile.seek(0)将跳转到文件开头。 - 如果您一次运行
await myfile.read()然后需要再次读取内容,这将特别有用。
- 例如,
close():关闭文件。
由于所有这些方法都是 async 方法,因此您需要“await”它们。
例如,在 async 路径操作函数中,您可以通过以下方式获取内容
contents = await myfile.read()
如果您在一个普通的 def 路径操作函数中,您可以直接访问 UploadFile.file,例如
contents = myfile.file.read()
async 技术细节
当您使用 async 方法时,FastAPI 会在线程池中运行文件方法并等待它们。
Starlette 技术细节
FastAPI 的 UploadFile 直接继承自 **Starlette** 的 UploadFile,但添加了一些必要的组件,使其与 **Pydantic** 和 FastAPI 的其他部分兼容。
什么是“表单数据”?¶
HTML 表单(<form></form>)将数据发送到服务器的方式通常使用一种“特殊”的编码方式,这与 JSON 不同。
FastAPI 将确保从正确的位置读取数据,而不是 JSON。
技术细节
当表单不包含文件时,来自表单的数据通常使用“媒体类型” application/x-www-form-urlencoded 进行编码。
但是,当表单包含文件时,它将以 multipart/form-data 编码。如果您使用 File,FastAPI 将知道它需要从正文的正确部分获取文件。
如果您想阅读更多关于这些编码和表单字段的信息,请访问 MDN 上的 POST 相关网页文档。
警告
您可以在一个路径操作中声明多个 File 和 Form 参数,但您也不能声明您期望作为 JSON 接收的 Body 字段,因为请求的正文将使用 multipart/form-data 而不是 application/json 进行编码。
这不是 FastAPI 的限制,它是 HTTP 协议的一部分。
可选文件上传¶
您可以通过使用标准的类型注解并将默认值设置为 None 来使文件成为可选的
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes | None, File()] = None):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}
🤓 其他版本和变体
from typing import Annotated, Union
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[Union[bytes, None], File()] = None):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: Union[UploadFile, None] = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes | None = File(default=None)):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}
提示
如果可能,请优先使用 Annotated 版本。
from typing import Union
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Union[bytes, None] = File(default=None)):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: Union[UploadFile, None] = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}
带有附加元数据的 UploadFile¶
您也可以将 File() 与 UploadFile 一起使用,例如,设置附加元数据
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes, File(description="A file read as bytes")]):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(
file: Annotated[UploadFile, File(description="A file read as UploadFile")],
):
return {"filename": file.filename}
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File(description="A file read as bytes")):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(
file: UploadFile = File(description="A file read as UploadFile"),
):
return {"filename": file.filename}
多文件上传¶
可以同时上传多个文件。
它们将与使用“表单数据”发送的同一个“表单字段”相关联。
要使用此功能,请声明一个 bytes 或 UploadFile 的列表
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(files: Annotated[list[bytes], File()]):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(files: list[bytes] = File()):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)
您将按声明的方式收到一个 list,其中包含 bytes 或 UploadFile 对象。
技术细节
您也可以使用 from starlette.responses import HTMLResponse。
FastAPI 提供与 fastapi.responses 相同的 starlette.responses 只是为了方便开发者。但大多数可用的响应直接来自 Starlette。
带有附加元数据的多文件上传¶
与之前相同的方式,您可以使用 File() 来设置附加参数,即使是对于 UploadFile
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(
files: Annotated[list[bytes], File(description="Multiple files as bytes")],
):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(
files: Annotated[
list[UploadFile], File(description="Multiple files as UploadFile")
],
):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(
files: list[bytes] = File(description="Multiple files as bytes"),
):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(
files: list[UploadFile] = File(description="Multiple files as UploadFile"),
):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)
总结¶
使用 File、bytes 和 UploadFile 来声明请求中要上传的文件,这些文件将作为表单数据发送。