microdot.multipart --- تحليل multipart/form-data

يحلل أجسام الطلبات من نوع Content-Type: multipart/form-data -- وهو الترميز الذي تستخدمه المتصفحات للنماذج التي تتضمن حقول <input type="file">. نكهتان من واجهة API:

  • FormDataIter تدفقية تُنتج الحقول واحداً تلو الآخر -- مفيدة عندما يتعين على التطبيق معالجة عمليات رفع ضخمة جداً جزءاً جزءاً على جهاز محدود الذاكرة.

  • مزخرف with_form_data() يخزّن كل شيء مؤقتاً ويملأ request.form / request.files -- وهو واجهة API الملائمة لعمليات الرفع ذات الحجم العادي.

class FormDataIter

class microdot.multipart.FormDataIter(request)

مكرِّر غير متزامن على أجزاء جسم request متعدد الأجزاء. القيم المُنتَجة هي أزواج (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)

تخزين الرفع مؤقتاً (إما في RAM أو في ملف مؤقت، اعتماداً على max_memory_size) بحيث يمكن تحليل بقية جسم الطلب متعدد الأجزاء دون أن يصبح التدفق الأصلي غير صالح. يستدعي مزخرف with_form_data() هذا تلقائياً.

async close()

تحرير أي ملف مؤقت أنشأه copy(). يُستدعى تلقائياً عند انتهاء الطلب إذا وصل الرفع إلى request.files عبر with_form_data().

مزخرفات على مستوى الوحدة

microdot.multipart.with_form_data(f)

مزخرف يحلل الجسم متعدد الأجزاء مسبقاً ويملأ request.form و request.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.files و request.form بحرية. تُنظَّف الملفات المؤقتة تلقائياً عند انتهاء الطلب.

للعمليات الرفع الأكبر من بضعة ميغابايت، فضّل واجهة FormDataIter التدفقية؛ إذ يجمع with_form_data() الطلب بأكمله في الذاكرة أو على نظام الملفات قبل تشغيل المعالج.