请求文件¶
您可以使用 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}
🤓 其他版本和变体
from fastapi import FastAPI, File, UploadFile
from typing_extensions import Annotated
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}
🤓 其他版本和变体
from fastapi import FastAPI, File, UploadFile
from typing_extensions import Annotated
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 正文,您需要使用 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}
🤓 其他版本和变体
from fastapi import FastAPI, File, UploadFile
from typing_extensions import Annotated
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}
使用 UploadFile
比 bytes
有几个优点
- 您无需在参数的默认值中使用
File()
。 - 它使用“假脱机”文件
- 文件存储在内存中,最大大小限制,超过此限制后将存储在磁盘中。
- 这意味着它适用于图像、视频、大型二进制文件等大文件,而不会消耗所有内存。
- 您可以从上传的文件中获取元数据。
- 它具有 文件类
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 Web 文档中的 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}
from typing import Union
from fastapi import FastAPI, File, UploadFile
from typing_extensions import Annotated
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}
🤓 其他版本和变体
from fastapi import FastAPI, File, UploadFile
from typing_extensions import Annotated
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)
🤓 其他版本和变体
from typing import List
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
from typing_extensions import Annotated
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)
提示
如果可能,请优先使用 Annotated
版本。
from typing import List
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)
您将按声明接收一个 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)
🤓 其他版本和变体
from typing import List
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
from typing_extensions import Annotated
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)
提示
如果可能,请优先使用 Annotated
版本。
from typing import List
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
声明要在请求中上传的文件,这些文件以表单数据形式发送。