Arquivos de Requisição¶
Você pode definir arquivos para serem enviados pelo cliente usando File
.
Informação
Para receber arquivos enviados, primeiro instale o python-multipart
.
Garanta que você criou um ambiente virtual, o ativou e então o instalou, por exemplo:
$ pip install python-multipart
Isso é necessário, visto que os arquivos enviados são enviados como "dados de formulário".
Importe File
¶
Importe File
e UploadFile
de fastapi
:
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}
🤓 Other versions and variants
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}
Tip
Prefer to use the Annotated
version if possible.
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}
Definir Parâmetros File
¶
Crie parâmetros de arquivo da mesma forma que você faria para Body
ou 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}
🤓 Other versions and variants
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}
Tip
Prefer to use the Annotated
version if possible.
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}
Informação
File
é uma classe que herda diretamente de Form
.
Mas lembre-se que quando você importa Query
, Path
, File
e outros de fastapi
, eles são, na verdade, funções que retornam classes especiais.
Dica
Para declarar corpos de arquivos, você precisa usar File
, caso contrário, os parâmetros seriam interpretados como parâmetros de consulta ou parâmetros de corpo (JSON).
Os arquivos serão enviados como "dados de formulário".
Se você declarar o tipo do parâmetro da função da sua operação de rota como bytes
, o FastAPI lerá o arquivo para você e você receberá o conteúdo como bytes
.
Mantenha em mente que isso significa que todo o conteúdo será armazenado na memória. Isso funcionará bem para arquivos pequenos.
Mas há muitos casos em que você pode se beneficiar do uso de UploadFile
.
Parâmetros de Arquivo com UploadFile
¶
Defina um parâmetro de arquivo com um tipo de 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}
🤓 Other versions and variants
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}
Tip
Prefer to use the Annotated
version if possible.
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}
Utilizar UploadFile
tem várias vantagens sobre bytes
:
- Você não precisa utilizar o
File()
no valor padrão do parâmetro. - Ele utiliza um arquivo "spooled":
- Um arquivo armazenado na memória até um limite máximo de tamanho, e após passar esse limite, ele será armazenado no disco.
- Isso significa que funcionará bem para arquivos grandes como imagens, vídeos, binários grandes, etc., sem consumir toda a memória.
- Você pode receber metadados do arquivo enviado.
- Ele tem uma file-like interface
assíncrona
. - Ele expõe um objeto python
SpooledTemporaryFile
que você pode passar diretamente para outras bibliotecas que esperam um objeto semelhante a um arquivo("file-like").
UploadFile
¶
UploadFile
tem os seguintes atributos:
filename
: Umastr
com o nome do arquivo original que foi enviado (por exemplo,myimage.jpg
).content_type
: Umastr
com o tipo de conteúdo (tipo MIME / tipo de mídia) (por exemplo,image/jpeg
).file
: UmSpooledTemporaryFile
(um file-like objeto). Este é o objeto de arquivo Python que você pode passar diretamente para outras funções ou bibliotecas que esperam um objeto semelhante a um arquivo("file-like").
UploadFile
tem os seguintes métodos assíncronos
. Todos eles chamam os métodos de arquivo correspondentes por baixo dos panos (usando o SpooledTemporaryFile
interno).
write(data)
: Escrevedata
(str
oubytes
) no arquivo.read(size)
: Lêsize
(int
) bytes/caracteres do arquivo.seek(offset)
: Vai para o byte na posiçãooffset
(int
) no arquivo.- Por exemplo,
await myfile.seek(0)
irá para o início do arquivo. - Isso é especialmente útil se você executar
await myfile.read()
uma vez e precisar ler o conteúdo novamente.
- Por exemplo,
close()
: Fecha o arquivo.
Como todos esses métodos são métodos assíncronos
, você precisa "aguardar" por eles.
Por exemplo, dentro de uma função de operação de rota assíncrona
, você pode obter o conteúdo com:
contents = await myfile.read()
Se você estiver dentro de uma função de operação de rota normal def
, você pode acessar o UploadFile.file
diretamente, por exemplo:
contents = myfile.file.read()
Detalhes Técnicos do async
Quando você usa os métodos async
, o FastAPI executa os métodos de arquivo em um threadpool e aguarda por eles.
"Detalhes Técnicos do Starlette"
O UploadFile
do FastAPI herda diretamente do UploadFile
do *Starlette** , mas adiciona algumas partes necessárias para torná-lo compatível com o Pydantic e as outras partes do FastAPI.
O que é "Form Data"¶
O jeito que os formulários HTML (<form></form>
) enviam os dados para o servidor normalmente usa uma codificação "especial" para esses dados, a qual é diferente do JSON.
FastAPI se certificará de ler esses dados do lugar certo, ao invés de JSON.
"Detalhes Técnicos"
Dados de formulários normalmente são codificados usando o "media type" (tipo de mídia) application/x-www-form-urlencoded
quando não incluem arquivos.
Mas quando o formulário inclui arquivos, ele é codificado como multipart/form-data
. Se você usar File
, o FastAPI saberá que tem que pegar os arquivos da parte correta do corpo da requisição.
Se você quiser ler mais sobre essas codificações e campos de formulário, vá para a MDN web docs para POST
.
Aviso
Você pode declarar múltiplos parâmetros File
e Form
em uma operação de rota, mas você não pode declarar campos Body
que você espera receber como JSON, pois a requisição terá o corpo codificado usando multipart/form-data
ao invés de application/json
.
Isso não é uma limitação do FastAPI, é parte do protocolo HTTP.
Upload de Arquivo Opcional¶
Você pode tornar um arquivo opcional usando anotações de tipo padrão e definindo um valor padrão de 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}
🤓 Other versions and variants
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}
Tip
Prefer to use the Annotated
version if possible.
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}
Tip
Prefer to use the Annotated
version if possible.
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
com Metadados Adicionais¶
Você também pode usar File()
com UploadFile
, por exemplo, para definir metadados adicionais:
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}
🤓 Other versions and variants
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}
Tip
Prefer to use the Annotated
version if possible.
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}
Uploads de Múltiplos Arquivos¶
É possível realizar o upload de vários arquivos ao mesmo tempo.
Eles serão associados ao mesmo "campo de formulário" enviado usando "dados de formulário".
Para usar isso, declare uma lista de bytes
ou 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)
🤓 Other versions and variants
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)
Tip
Prefer to use the Annotated
version if possible.
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)
Tip
Prefer to use the Annotated
version if possible.
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)
Você receberá, tal como declarado, uma list
de bytes
ou UploadFile
.
"Detalhes Técnicos"
Você pode também pode usar from starlette.responses import HTMLResponse
.
FastAPI providencia o mesmo starlette.responses
que fastapi.responses
apenas como uma conveniência para você, o desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
Uploads de Múltiplos Arquivos com Metadados Adicionais¶
Da mesma forma de antes, você pode usar File()
para definir parâmetros adicionais, mesmo para 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)
🤓 Other versions and variants
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)
Tip
Prefer to use the Annotated
version if possible.
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)
Tip
Prefer to use the Annotated
version if possible.
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)
Recapitulando¶
Utilize File
, bytes
e UploadFile
para declarar arquivos a serem enviados na requisição, enviados como dados de formulário.