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

解析 Content-Type: multipart/form-data 請求主體 —— 瀏覽器用於包含 <input type="file"> 欄位之表單的編碼方式。提供兩種形式的 API:

  • 串流式的 FormDataIter,逐一產生各欄位 —— 當應用程式必須在記憶體受限的裝置上逐塊處理極大型上傳時相當有用。

  • with_form_data() 裝飾器,會緩衝所有內容並填入 request.formrequest.files —— 適用於一般大小上傳的便利 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)

單一個已上傳的檔案。實例由 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。暫存檔會在請求結束時自動清理。

對於大於數 MB 的上傳,建議優先使用串流式的 FormDataIter API;with_form_data() 會在處理常式執行前,將整個請求累積於記憶體或檔案系統中。