microdot.multipart --- multipart/form-data の解析

Content-Type: multipart/form-data のリクエストボディ -- <input type="file"> フィールドを含むフォームでブラウザが使用するエンコーディング -- を解析します。2 種類の API があります:

  • フィールドを 1 つずつ生成するストリーミング型の FormDataIter。メモリに制約のあるデバイス上で、アプリケーションが非常に大きなアップロードを少しずつ処理する必要がある場合に便利です。

  • すべてをバッファリングして request.formrequest.files を設定する with_form_data() デコレータ。通常サイズのアップロードに便利な API です。

class FormDataIter

class microdot.multipart.FormDataIter(request)

request の multipart ボディの各パートに対する非同期イテレータ。生成される値は (name, value) タプルです。value は通常のフィールドでは str、ファイルフィールドでは FileUpload です。

使いやすさよりメモリが重要な場合に直接使用します:

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'

注釈

ファイルフィールドを反復処理する際は、次の async for の反復の前にファイルを(FileUpload.read() または FileUpload.save() を介して)消費する必要があります。反復が進むと、基盤となるストリームは無効になります。

buffer_size: int

クラス属性。リクエストストリームから読み取る際に使用するチャンクサイズ。デフォルトは 256 バイトです。RAM を犠牲にしてスループットを高めるには増やしてください。

class FileUpload

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

アップロードされた 1 つのファイル。インスタンスは FormDataIter によって生成され、with_form_data() によって request.files に収集されます。通常、アプリケーションが FileUpload を直接構築することはありません。

filename: str

クライアントが送信したままのファイルの元の名前(信頼できません -- サニタイズせずに open() に渡さないでください)。

content_type: str | None

パートの Content-Type ヘッダーからの MIME タイプ。提供されていない場合は None

max_memory_size: int

クラス属性。copy() がメモリ内バッファリングから一時ファイルへ切り替わるしきい値(バイト単位)。デフォルトは 1024 です。

async read(n: int = -1)

アップロードストリームから最大 n バイトを読み取ります。-1 で末尾まで読み取ります。

async save(path_or_file)

アップロードを path_or_file に保存します。これはファイルシステムのパス、または既に開いているファイルオブジェクトのいずれかです。

async copy(max_memory_size: int | None = None)

アップロードを(max_memory_size に応じて RAM または一時ファイルに)バッファリングし、元のストリームを無効にすることなく multipart ボディの残りを解析できるようにします。with_form_data() デコレータはこれを自動的に呼び出します。

async close()

copy() によって作成された一時ファイルがあれば解放します。アップロードが with_form_data() を介して request.files に到達した場合、リクエスト終了時に自動的に呼び出されます。

モジュールレベルのデコレータ

microdot.multipart.with_form_data(f)

ハンドラが実行される前に multipart ボディを先に解析し、request.formrequest.files を設定するデコレータ:

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'

ファイルアップロードは FileUpload.copy() を介してバッファリングされるため、ハンドラは request.filesrequest.form を自由に反復処理できます。一時ファイルはリクエスト終了時に自動的にクリーンアップされます。

数メガバイトを超えるアップロードには、ストリーミング型の FormDataIter API を推奨します。with_form_data() は、ハンドラが実行される前にリクエスト全体をメモリまたはファイルシステム上に蓄積します。