microdot.multipart — ניתוח multipart/form-data

מנתח גופי בקשה מסוג Content-Type: multipart/form-data – הקידוד שדפדפנים משתמשים בו עבור טפסים הכוללים שדות <input type="file">. שני סוגים של API:

  • FormDataIter בזרימה שמניב שדות בזה אחר זה – שימושי כאשר היישום צריך לטפל בהעלאות גדולות מאוד חתיכה אחר חתיכה בהתקן מוגבל-זיכרון.

  • דקורטור with_form_data() שמחזיק הכול בחוצץ (buffer) ומאכלס את request.form / request.files – ה-API הנוח עבור העלאות בגודל רגיל.

class FormDataIter

class microdot.multipart.FormDataIter(request)

איטרטור אסינכרוני על החלקים של גוף ה-multipart של request. הערכים המונבים הם tuples מסוג (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'

הערה

בעת איטרציה על שדות קובץ, יש לצרוך את הקובץ (באמצעות FileUpload.read() או FileUpload.save()) לפני איטרציית ה-async for הבאה – הזרם הבסיסי מתבטל כאשר האיטרציה מתקדמת.

buffer_size: int

תכונת מחלקה. גודל ה-chunk המשמש בעת קריאה מזרם הבקשה. ברירת מחדל 256 בתים. הגדילו עבור תפוקה גבוהה יותר במחיר של RAM.

class FileUpload

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

קובץ יחיד שהועלה. מופעים מונבים על ידי FormDataIter ונאספים אל request.files על ידי with_form_data(). יישומים בדרך כלל אינם בונים FileUpload ישירות.

filename: str

השם המקורי של הקובץ כפי שהלקוח שלח אותו (לא מהימן – אל תעבירו ל-open() ללא חיטוי).

content_type: str | None

סוג ה-MIME מכותרת ה-Content-Type של החלק, או 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)

מחזיקה את ההעלאה בחוצץ (buffer) (ב-RAM או בקובץ זמני, בהתאם ל-max_memory_size) כך שניתן לנתח את שאר גוף ה-multipart מבלי שהזרם המקורי יתבטל. הדקורטור with_form_data() קורא לזה אוטומטית.

async close()

משחררת כל קובץ זמני שנוצר על ידי copy(). נקראת אוטומטית כאשר הבקשה מסתיימת אם ההעלאה הגיעה אל request.files באמצעות with_form_data().

דקורטורים ברמת המודול

microdot.multipart.with_form_data(f)

דקורטור שמנתח את גוף ה-multipart מראש ומאכלס את request.form ואת request.files לפני שה-handler רץ:

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'

העלאות קבצים מוחזקות בחוצץ (buffer) באמצעות FileUpload.copy(), כך שה-handler יכול לבצע איטרציה על request.files ו-request.form בחופשיות. קבצים זמניים מנוקים אוטומטית כאשר הבקשה מסתיימת.

עבור העלאות גדולות מכמה מגה-בתים, העדיפו את ה-API FormDataIter בזרימה; with_form_data() צובר את כל הבקשה בזיכרון או במערכת הקבצים לפני שה-handler רץ.