microdot.multipart — análisis de multipart/form-data

Analiza los cuerpos de solicitud Content-Type: multipart/form-data: la codificación que usan los navegadores para los formularios que incluyen campos <input type="file">. Dos variantes de la API:

  • Un FormDataIter de transmisión que produce los campos uno a la vez: útil cuando la aplicación tiene que gestionar cargas muy grandes pieza por pieza en un dispositivo con memoria limitada.

  • Un decorador with_form_data() que almacena en búfer todo y rellena request.form / request.files: la API cómoda para cargas de tamaño normal.

class FormDataIter

class microdot.multipart.FormDataIter(request)

Iterador asíncrono sobre las partes del cuerpo multipart de request. Los valores producidos son tuplas (name, value); value es un str para los campos normales y un FileUpload para los campos de archivo.

Se usa directamente cuando la memoria importa más que la ergonomía:

from microdot.multipart import FormDataIter, FileUpload

@app.post('/upload')
async def upload(request):
    async for name, value in FormDataIter(request):
        if isinstance(value, FileUpload):
            await value.save('/sdcard/' + value.filename)
        else:
            print(name, '=', value)
    return 'ok'

Nota

Al iterar sobre los campos de archivo, el archivo debe consumirse (mediante FileUpload.read() o FileUpload.save()) antes de la siguiente iteración async for: el flujo subyacente se invalida cuando avanza la iteración.

buffer_size: int

Atributo de clase. Tamaño del fragmento usado al leer del flujo de la solicitud. Por defecto 256 bytes. Auméntelo para obtener mayor rendimiento a costa de RAM.

class FileUpload

class microdot.multipart.FileUpload(filename: str, content_type: str | None, read)

Un único archivo cargado. Las instancias son producidas por FormDataIter y recopiladas en request.files por with_form_data(). Las aplicaciones normalmente no construyen FileUpload directamente.

filename: str

El nombre original del archivo tal como lo envió el cliente (no fiable: no lo pase a open() sin sanearlo).

content_type: str | None

El tipo MIME del encabezado Content-Type de la parte, o None si no se proporciona.

max_memory_size: int

Atributo de clase. Umbral (en bytes) por encima del cual copy() cambia del almacenamiento en búfer en memoria a un archivo temporal. Por defecto 1024.

async read(n: int = -1)

Lee hasta n bytes del flujo de carga. -1 lee hasta el final.

async save(path_or_file)

Guarda la carga en path_or_file, que puede ser una ruta del sistema de archivos o un objeto de archivo ya abierto.

async copy(max_memory_size: int | None = None)

Almacena la carga en búfer (ya sea en RAM o en un archivo temporal, según max_memory_size) para que el resto del cuerpo multipart pueda analizarse sin que el flujo original se invalide. El decorador with_form_data() llama a esto automáticamente.

async close()

Libera cualquier archivo temporal creado por copy(). Se llama automáticamente cuando la solicitud finaliza si la carga llegó a request.files mediante with_form_data().

Decoradores a nivel de módulo

microdot.multipart.with_form_data(f)

Decorador que analiza el cuerpo multipart por adelantado y rellena request.form y request.files antes de que se ejecute el controlador:

from microdot import Microdot
from microdot.multipart import with_form_data

app = Microdot()

@app.post('/upload')
@with_form_data
async def upload(request):
    print('fields:', dict(request.form))
    for name, file in request.files.items():
        await file.save('/sdcard/' + sanitize(file.filename))
    return 'ok'

Las cargas de archivos se almacenan en búfer mediante FileUpload.copy(), de modo que el controlador puede iterar request.files y request.form libremente. Los archivos temporales se limpian automáticamente cuando la solicitud finaliza.

Para cargas mayores que un par de megabytes, prefiera la API de transmisión FormDataIter; with_form_data() acumula toda la solicitud en memoria o en el sistema de archivos antes de que se ejecute el controlador.