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() الطلب بأكمله في الذاكرة أو على نظام الملفات قبل تشغيل المعالج.