Saltar al contenido

Request Files (Archivos de Request)

Puedes definir archivos para ser subidos por el cliente usando File.

Nota

Para recibir archivos subidos, primero instala python-multipart.

Asegúrate de crear un entorno virtual, activarlo, y luego instalarlo, por ejemplo:

$ pip install python-multipart

Esto se debe a que los archivos subidos se envían como "form data".

Importar File

Importa File y 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}
🤓 Otras versiones y variantes

Consejo

Preferible usar la versión con Annotated si es posible.

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

Crea parámetros de archivo de la misma manera que lo harías para Body o 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}
🤓 Otras versiones y variantes

Consejo

Preferible usar la versión con Annotated si es posible.

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}

Nota

File es una clase que hereda directamente de Form.

Pero recuerda que cuando importas Query, Path, File y otros de fastapi, esos son en realidad funciones que devuelven clases especiales.

Consejo

Para declarar cuerpos de File, necesitas usar File, porque de lo contrario los parámetros serían interpretados como parámetros de query o de cuerpo (JSON).

Los archivos se subirán como "form data".

Si declaras el tipo del parámetro de tu función de path operation como bytes, FastAPI leerá el archivo por ti y recibirás el contenido como bytes.

Ten en cuenta que esto significa que todo el contenido se almacenará en memoria. Esto funcionará bien para archivos pequeños.

Pero hay varios casos en los que podrías beneficiarte de usar UploadFile.

Parámetros File con UploadFile

Define un parámetro de archivo con un tipo 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}
🤓 Otras versiones y variantes

Consejo

Preferible usar la versión con Annotated si es posible.

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}

Usar UploadFile tiene varias ventajas sobre bytes:

  • No tienes que usar File() en el valor por defecto del parámetro.
  • It uses a "spooled" file:
    • Un archivo almacenado en memoria hasta un límite máximo de tamaño, y después de superar este límite se almacenará en disco.
  • Esto significa que funcionará bien para archivos grandes como imágenes, videos, binarios grandes, etc. sin consumir toda la memoria.
  • Puedes obtener metadatos del archivo subido.
  • Tiene una interfaz async tipo archivo.
  • Expone un objeto SpooledTemporaryFile de Python real que puedes pasar directamente a otras librerías que esperan un objeto tipo archivo.

UploadFile

UploadFile tiene los siguientes atributos:

  • filename: Un str con el nombre original del archivo subido (ej. myimage.jpg).
  • content_type: Un str con el tipo de contenido (tipo MIME / media type) (ej. image/jpeg).
  • file: Un SpooledTemporaryFile (un objeto tipo archivo). Este es el objeto archivo real de Python que puedes pasar directamente a otras funciones o librerías que esperan un objeto "tipo archivo".

UploadFile tiene los siguientes métodos async. Todos llaman a los métodos de archivo correspondientes por debajo (usando el SpooledTemporaryFile interno).

  • write(data): Escribe data (str o bytes) en el archivo.
  • read(size): Lee size (int) bytes/caracteres del archivo.
  • seek(offset): Goes to the byte position offset (int) in the file.
    • Ej., await myfile.seek(0) iría al inicio del archivo.
    • Esto es especialmente útil si ejecutas await myfile.read() una vez y luego necesitas leer el contenido de nuevo.
  • close(): Cierra el archivo.

Como todos estos métodos son métodos async, necesitas "await"earlos.

Por ejemplo, dentro de una función de path operation async puedes obtener el contenido con:

contents = await myfile.read()

Si estás dentro de una función de path operation def normal, puedes acceder a UploadFile.file directamente, por ejemplo:

contents = myfile.file.read()

Detalles Técnicos de async

Cuando usas los métodos async, FastAPI ejecuta los métodos de archivo en un threadpool y los espera.

Detalles Técnicos de Starlette

El UploadFile de FastAPI hereda directamente del UploadFile de Starlette, pero añade algunas partes necesarias para hacerlo compatible con Pydantic y las otras partes de FastAPI.

Qué es "Form Data"

La forma en que los formularios HTML (<form></form>) envían los datos al servidor normalmente usa una codificación "especial" para esos datos, es diferente de JSON.

FastAPI se asegurará de leer esos datos del lugar correcto en lugar de JSON.

Detalles Técnicos

Los datos de los formularios se codifican normalmente usando el "media type" application/x-www-form-urlencoded cuando no incluyen archivos.

Pero cuando el formulario incluye archivos, se codifica como multipart/form-data. Si usas File, FastAPI sabrá que tiene que obtener los archivos de la parte correcta del cuerpo.

Si quieres leer más sobre estas codificaciones y campos de formulario, dirígete a la documentación web de MDN para POST.

Aviso

Puedes declarar múltiples parámetros File y Form en una path operation, pero no puedes también declarar campos Body que esperas recibir como JSON, ya que la request tendrá el cuerpo codificado usando multipart/form-data en lugar de application/json.

Esto no es una limitación de FastAPI, es parte del protocolo HTTP.

Subida Opcional de Archivos

Puedes hacer que un archivo sea opcional usando anotaciones de tipo estándar y configurando un valor por defecto 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}
🤓 Otras versiones y variantes

Consejo

Preferible usar la versión con Annotated si es posible.

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}

UploadFile con Metadatos Adicionales

También puedes usar File() con UploadFile, por ejemplo, para configurar metadatos adicionales:

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}
🤓 Otras versiones y variantes

Consejo

Preferible usar la versión con Annotated si es posible.

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}

Subida de Múltiples Archivos

Es posible subir varios archivos al mismo tiempo.

Se asociarían al mismo "form field" enviado usando "form data".

Para usarlo, declara una lista de bytes o 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)
🤓 Otras versiones y variantes

Consejo

Preferible usar la versión con Annotated si es posible.

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)

Recibirás, como se declaró, una list de bytes o UploadFiles.

Detalles Técnicos

También podrías usar from starlette.responses import HTMLResponse.

FastAPI proporciona las mismas starlette.responses que fastapi.responses solo como una conveniencia para ti, el desarrollador. Pero la mayoría de las respuestas disponibles vienen directamente de Starlette.

Subida de Múltiples Archivos con Metadatos Adicionales

Y de la misma manera que antes, puedes usar File() para configurar parámetros adicionales, incluso 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)
🤓 Otras versiones y variantes

Consejo

Preferible usar la versión con Annotated si es posible.

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)

Resumen

Usa File, bytes, y UploadFile para declarar archivos que serán subidos en la request, enviados como form data.