microdot.multipart — Parsen von multipart/form-data

Parst Anfrage-Bodies vom Typ Content-Type: multipart/form-data – die Kodierung, die Browser für Formulare mit <input type="file">-Feldern verwenden. Zwei Varianten der API:

  • Ein streamender FormDataIter, der die Felder einzeln liefert – nützlich, wenn die Anwendung sehr große Uploads stückweise auf einem speicherbeschränkten Gerät verarbeiten muss.

  • Ein with_form_data()-Dekorator, der alles puffert und request.form / request.files füllt – die bequeme API für Uploads normaler Größe.

class FormDataIter

class microdot.multipart.FormDataIter(request)

Asynchroner Iterator über die Teile des Multipart-Bodys von request. Die gelieferten Werte sind (name, value)-Tupel; value ist ein str für reguläre Felder und ein FileUpload für Dateifelder.

Wird direkt verwendet, wenn Speicher wichtiger ist als Ergonomie:

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'

Bemerkung

Beim Iterieren über Dateifelder muss die Datei (über FileUpload.read() oder FileUpload.save()) vollständig gelesen werden, bevor die nächste async for-Iteration erfolgt – der zugrunde liegende Stream wird ungültig, sobald die Iteration fortschreitet.

buffer_size: int

Klassenattribut. Beim Lesen aus dem Anfrage-Stream verwendete Blockgröße. Standard 256 Bytes. Erhöhe sie für einen höheren Durchsatz zulasten des RAM.

class FileUpload

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

Eine einzelne hochgeladene Datei. Instanzen werden von FormDataIter geliefert und von with_form_data() in request.files gesammelt. Anwendungen erzeugen FileUpload normalerweise nicht direkt.

filename: str

Der ursprüngliche Name der Datei, wie der Client ihn gesendet hat (nicht vertrauenswürdig – nicht ohne Bereinigung an open() übergeben).

content_type: str | None

Der MIME-Typ aus dem Content-Type-Header des Teils oder None, falls nicht angegeben.

max_memory_size: int

Klassenattribut. Schwellenwert (in Bytes), oberhalb dessen copy() von der Pufferung im Speicher auf eine temporäre Datei umschaltet. Standard 1024.

async read(n: int = -1)

Liest bis zu n Bytes aus dem Upload-Stream. -1 liest bis zum Ende.

async save(path_or_file)

Speichert den Upload in path_or_file, was ein Dateisystempfad oder ein bereits geöffnetes Dateiobjekt sein kann.

async copy(max_memory_size: int | None = None)

Puffert den Upload (entweder im RAM oder in einer temporären Datei, abhängig von max_memory_size), sodass der Rest des Multipart-Bodys geparst werden kann, ohne dass der ursprüngliche Stream ungültig wird. Der with_form_data()-Dekorator ruft dies automatisch auf.

async close()

Gibt jede von copy() erstellte temporäre Datei frei. Wird automatisch aufgerufen, wenn die Anfrage abgeschlossen ist, sofern der Upload über with_form_data() in request.files gelangt ist.

Dekoratoren auf Modulebene

microdot.multipart.with_form_data(f)

Dekorator, der den Multipart-Body vorab parst und request.form und request.files füllt, bevor der Handler ausgeführt wird:

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'

Datei-Uploads werden über FileUpload.copy() gepuffert, sodass der Handler frei über request.files und request.form iterieren kann. Temporäre Dateien werden automatisch bereinigt, wenn die Anfrage endet.

Bevorzuge für Uploads, die größer als ein paar Megabyte sind, die streamende FormDataIter-API; with_form_data() sammelt die gesamte Anfrage im Speicher oder im Dateisystem an, bevor der Handler ausgeführt wird.