Multipart Upload في تطوير الويب: الجوهر والهيكل وكيف يعمل multipart/form-data

المؤلف: IT Sectr نُشر: 2026-03-10 وقت القراءة: 9 دق

Multipart Upload هي آلية HTTP تسمح بنقل عدة أجزاء غير متجانسة من البيانات في طلب واحد، بما في ذلك الحقول النصية والملفات الثنائية. يتم فصل كل جزء بسلسلة حد فريدة وله رأس Content-Type خاص به. وفقًا لـ MDN Web Docs، 2025، multipart/form-data هو التنسيق القياسي لتحميل الملفات عبر نماذج HTML ويستخدم على نطاق واسع في تطبيقات الويب والجوال لإرسال الصور والمستندات والملفات الأخرى إلى الخادم.

النقاط الرئيسية

  • Multipart Upload — نقل أجزاء متعددة من البيانات في طلب HTTP واحد مفصولة بحد.
  • multipart/form-data — نوع MIME القياسي لتحميل الملفات من نماذج HTML وتطبيقات الجوال.
  • الحد (Boundary) — سلسلة فريدة تفصل أجزاء الطلب المركب، يتم إنشاؤها تلقائيًا بواسطة عملاء HTTP.
  • كل جزء يحتوي على رؤوس Content-Disposition و Content-Type التي تصف اسم الحقل ونوع الملف.
  • Multipart Upload أكثر كفاءة من الطلبات المتعددة — طلب POST واحد يحل محل N من الاستدعاءات المنفصلة للخادم.

ما هو Multipart Upload؟

Multipart Upload هي طريقة لنقل البيانات عبر بروتوكول HTTP حيث يتكون جسم الطلب من عدة أجزاء مفصولة منطقيًا. يمكن أن يحتوي كل جزء على بيانات من نوع مختلف: حقل نموذج نصي، ملف ثنائي، كائن JSON أو صورة. يتم تجميع جميع الأجزاء في طلب POST واحد، مما يلغي الحاجة إلى إرسال N من استدعاءات HTTP المنفصلة. Multipart Upload هو جزء أساسي من نماذج الويب وواجهات برمجة التطبيقات لتحميل الملفات.

تم تعريف تنسيق multipart في مواصفة RFC 2046 كجزء من معيار MIME لرسائل البريد الإلكتروني، ثم تم تكييفه لـ HTTP في RFC 1867. اليوم، يستخدم تطوير الويب حصريًا تقريبًا multipart/form-data — أحد الأنواع الفرعية لـ multipart المصممة للنماذج التي تحتوي على ملفات. الأنواع الفرعية الأخرى — multipart/mixed (للمرفقات التعسفية) و multipart/byteranges (للتنزيل الجزئي للملفات) — تُستخدم بشكل أقل تكرارًا.

الفرق الجوهري بين multipart و application/x-www-form-urlencoded هو أن الأخير يشفر جميع البيانات في سلسلة متوافقة مع URI ولا يدعم الملفات الثنائية. Multipart/form-data، على العكس، ينقل كل ملف في شكله الثنائي الأصلي دون تشفير، وهو أكثر كفاءة ولا يفقد الدقة. حجم الطلب مع multipart دائمًا أكبر بنسبة 5-15% من مجموع أحجام الملفات بسبب الحمل الإضافي لرؤوس الأجزاء والحدود.

متى يتم استخدام Multipart Upload

Multipart Upload يُستخدم في كل مكان يتطلب تحميل الملفات: الصور الرمزية وصور الملف الشخصي في شبكات التواصل الاجتماعي، المرفقات في تطبيقات المراسلة، المستندات في أنظمة CRM، صور المنتجات في المتاجر الإلكترونية. في تطبيقات الجوال، يُستخدم Multipart Upload لإرسال ملفات الوسائط إلى الخادم — صور من كاميرا الجهاز، تسجيلات صوتية، مقاطع فيديو. وفقًا لـ Cloudflare Research، حوالي 15% من جميع طلبات POST على الويب تستخدم multipart/form-data.

الفرق بين multipart والنقل المجزأ (Chunked Transfer)

Multipart Upload و Chunked Transfer هما آليتان مختلفتان. Multipart يقسم الطلب إلى أجزاء ذات محتوى (حقول وملفات)، بينما Chunked Transfer يقسم دفق البيانات إلى أجزاء للنقل دون معرفة الحجم الإجمالي. يمكن نقل Multipart داخل Chunked Transfer: يرسل الخادم استجابة multipart على أجزاء دون معرفة حجمها الكامل. هاتان الآليتان لا تتعارضان وتحلان مشاكل مختلفة على مستويات مختلفة.

كيف يعمل multipart/form-data

عندما يرسل المتصفح نموذجًا بالسمة enctype="multipart/form-data"، يقوم ببناء جسم الطلب بتنسيق multipart. يصبح كل حقل في النموذج كتلة منفصلة، مفصولة عن الأخرى بسلسلة حد (boundary). يتم إنشاء الحد تلقائيًا وهو عبارة عن تسلسل فريد من الأحرف يضمن عدم ظهوره داخل البيانات. العميل يضيف هذا الحد إلى رأس Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX7K.

تبدأ كل كتلة بـ --boundary وتحتوي على رؤوس Content-Disposition مع اسم الحقل (name) وللملفات، اسم الملف الأصلي (filename). بعد سطر فارغ تأتي بيانات الحقل أو محتوى الملف في شكل ثنائي. ينتهي الطلب بالسلسلة --boundary--. الخادم يحلل التدفق المستلم: أولاً يجد الحد، ثم يستخرج رؤوس كل جزء، ويحدد نوع البيانات ويمررها إلى معالج النموذج أو وحدة تحكم API.

وفقًا لـ IETF RFC 7578، لا يتطلب multipart/form-data تحديد مجموعة أحرف (charset) لكل جزء، حيث تعتبر الحقول النصية بـ UTF-8 والأجزاء الثنائية تحتوي على ملفات بترميزها الأصلي. حجم الجزء الواحد غير محدود بالبروتوكول — يتم تعيين الحدود على مستوى الخادم: على سبيل المثال، في Nginx عبر client_max_body_size، في Spring Boot عبر spring.servlet.multipart.max-file-size.

تنسيق الحد (Boundary) وتوليده

الحد (Boundary) هو سلسلة فريدة لا يجب أن تظهر في البيانات المنقولة. عادةً ما تبدأ ببادئة (مثل ----WebKitFormBoundary أو ----Boundary) وتحتوي على أحرف عشوائية. تقوم المتصفحات وعملاء HTTP بتوليد الحد تلقائيًا. يجب ألا يتجاوز طول الحد 70 حرفًا وفقًا لـ RFC 2046. يتم فصل كل جزء بالسلسلة --boundary\r\n، وتنتهي نهاية الطلب بالسلسلة --boundary--\r\n.

هيكل طلب multipart

طلب multipart له هيكل صارم محدد بمعايير MIME و HTTP. يحدد رأس الطلب Content-Type: multipart/form-data مع معامل boundary. يتكون جسم الطلب من سلسلة من الأجزاء، يحتوي كل منها على رؤوس وجسم خاصين به. رؤوس الجزء تشمل Content-Disposition (إلزامي) و Content-Type (اختياري — للملفات). وجود سطر فارغ بين رؤوس الجزء وبياناته إلزامي.

العنصرمثالالإلزامية
Content-Typemultipart/form-data; boundary=---Bnd123نعم
فاصل الجزء---Bnd123نعم (قبل كل جزء)
Content-Dispositionform-data; name="avatar"; filename="photo.jpg"نعم
Content-Type للجزءimage/jpegللملفات
جسم الجزء[بيانات الصورة الثنائية]نعم
الحد الختامي---Bnd123--نعم (نهاية الطلب)

مثال على طلب multipart

لننظر في مثال حقيقي لطلب multipart يرسل حقلاً نصيًا وملف صورة. العميل يشكل رأس Content-Type مع حد فريد. يحتوي جسم الطلب بالتسلسل على جميع حقول النموذج. عند الاستلام، يحلل الخادم هذه الأجزاء ويوفر للمطور إمكانية الوصول إلى كل حقل ككائن منفصل. يسمح هذا النهج بمعالجة النماذج المعقدة مع الملفات في استدعاء HTTP واحد.

kotlin
import okhttp3.*
import java.io.File

fun uploadFile() {
    val client = OkHttpClient()
    val imageFile = File("/path/to/photo.jpg")

    val requestBody = MultipartBody.Builder()
        .setType(MediaType.parse("multipart/form-data"))
        .addFormDataPart("username", "john_doe")
        .addFormDataPart(
            "avatar", "photo.jpg",
            RequestBody.create(
                MediaType.parse("image/jpeg"), imageFile
            )
        )
        .build()

    val request = Request.Builder()
        .url("https://api.example.com/upload")
        .post(requestBody)
        .build()

    client.newCall(request).execute().use { response ->
        println("تم الرفع: ${response.isSuccessful}")
    }
}

تحليل استجابة multipart على الخادم

على جانب الخادم، يتم تحليل طلب multipart بواسطة الإطار أو يدويًا. في Spring Boot، يكفي التعليق التوضيحي @RequestParam("avatar") MultipartFile file، ويقوم الإطار تلقائيًا باستخراج الملف من طلب multipart. في Ktor على Kotlin، يُستخدم receiveMultipart()، في Express.js — الوسيط multer. يحصل الخادم على إمكانية الوصول إلى كل حقل نموذج وكل ملف تم تحميله بشكل مستقل، ويحفظ الملف على القرص أو في التخزين السحابي ويعيد عنوان URL أو معرّف إلى العميل.

مزايا التحميل متعدد المكونات

يقدم Multipart Upload العديد من المزايا الرئيسية مقارنة بطرق نقل البيانات البديلة. طلب واحد بدلاً من العديد — يتم نقل جميع حقول النموذج والملفات في استدعاء HTTP واحد، مما يقلل الحمل على الشبكة والخادم. ليست هناك حاجة لفتح N اتصال لتحميل N من الملفات — يتم تعبئة كل شيء في طلب POST واحد. هذا مهم بشكل خاص لتطبيقات الجوال، حيث يعني كل اتصال HTTP تأخيرًا واستهلاكًا للبطارية.

نقل ثنائي بدون تشفير — على عكس application/x-www-form-urlencoded، حيث يتم تشفير البيانات الثنائية بـ base64 (زيادة الحجم بنسبة 33٪)، ينقل multipart/form-data الملفات في شكلها الثنائي الأصلي. هذا أكثر كفاءة من حيث الحجم والسرعة. بالنسبة للملفات الكبيرة التي يزيد حجمها عن 10 ميجابايت، يصبح الفرق حاسمًا: سيكون طلب multipart أصغر بنسبة 30٪ من طلب مشفر بعنوان URL مع نفس الملف.

هيكل تعسفي — يسمح multipart بدمج حقول من أنواع مختلفة بأي ترتيب. يمكن أن يحتوي النموذج على حقول نصية، وملفات متعددة، وبيانات JSON، وحقول مخفية في وقت واحد. كل جزء له Content-Type الخاص به، مما يسمح بخلط البيانات النصية والثنائية. للمقارنة: تشفير base64 يضيف 33٪ إلى الحجم، بينما يضيف multipart حوالي 5-15٪ فقط للرؤوس الخدمية.

مقارنة multipart مع تنسيقات النقل الأخرى

وفقًا لبحث HTTP Archive، 2025، يُستخدم multipart/form-data في 94٪ من حالات تحميل الملفات على الويب. البدائل — base64 في JSON (4٪) والنقل المباشر عبر WebSocket (2٪). JSON مع base64 مناسب لواجهات API حيث تكون جميع البيانات الأخرى أيضًا بتنسيق JSON، لكنه غير فعال للملفات الكبيرة. WebSocket مناسب للبيانات في الوقت الفعلي لكنه غير مدعوم من جميع البنى التحتية لـ HTTP. يبقى multipart المعيار لتحميل الملفات بفضل بساطته وكفاءته.

Multipart Upload في تطوير التطبيقات الجوالة

في تطبيقات الجوال، يُستخدم Multipart Upload لإرسال محتوى الوسائط من أجهزة المستخدمين: صور من المعرض، لقطات من الكاميرا، تسجيلات صوتية، ملفات مستندات. على Android، الطريقة القياسية هي OkHttp مع MultipartBody.Builder، الذي يسمح بتشكيل طلبات multipart بسهولة. يدعم Retrofit أيضًا multipart من خلال التعليقات التوضيحية @Multipart و @Part. يحدد المطور نوع البيانات لكل جزء، ويقوم عميل HTTP تلقائيًا بتوليد الرؤوس الصحيحة.

على iOS، يتم حل نفس المهام عبر URLSession مع HTTPBodyStream مخصص أو عبر Alamofire مع multipartFormData. يوفر Alamofire طريقة ملائمة upload(multipartFormData:) لإرسال طلبات multipart. في كلا المنصتين، من المهم مراعاة حجم الملفات التي يتم تحميلها — للملفات الكبيرة (أكثر من 10-20 ميجابايت)، يُنصح باستخدام التحميل في الخلفية حتى لا يتم إنهاء التطبيق عند التصغير. على Android، يتم ذلك عبر DownloadManager أو WorkManager، على iOS — عبر URLSession مع تكوين الخلفية.

عند تحميل الملفات في تطبيقات الجوال، يجب مراعاة حالة الشبكة. Connectivity Manager على Android يساعد في تحديد ما إذا كان Wi-Fi أو بيانات الجوال متاحة واختيار الوقت الأمثل للتحميل. للملفات الكبيرة مثل مقاطع الفيديو، يُنصح بتأجيل التحميل حتى الاتصال بشبكة Wi-Fi لعدم استهلاك بيانات الجوال للمستخدم. يسمح WorkManager على Android بتعيين مثل هذه القيود عبر NetworkType.UNMETERED.

تحسين التحميل: الضغط وتغيير الحجم

قبل إرسال ملف عبر Multipart Upload، غالبًا ما تقوم تطبيقات الجوال بضغط وتغيير حجم الصورة. ضغط JPEG بجودة 85٪ يقلل حجم الملف بمقدار 3-5 مرات دون فقدان ملحوظ في الجودة للعرض على الشاشة. تغيير حجم الصورة إلى 1920 بكسل على الجانب الأكبر يقلل الحجم أكثر. على Android، يتم ذلك باستخدام Bitmap.compress()، على iOS — UIImageJPEGRepresentation مع معامل ضغط 0.85. هذا التحسين يسرع التحميل ويوفر بيانات الجوال.

أخطاء وقيود Multipart Upload

الخطأ الأكثر شيوعًا في Multipart Upload هو تجاوز حد حجم الطلب على الخادم. بشكل افتراضي، يحد Nginx حجم جسم الطلب بـ 1 ميجابايت (client_max_body_size)، و Tomcat بـ 2 ميجابايت (maxSwallowSize). إذا لم يقم المطور بزيادة هذه الحدود، سيعيد الخادم خطأ 413 Request Entity Too Large. الحل هو تكوين الحد الأقصى لحجم التحميل على الخادم بشكل صريح وعرض تحذير على العميل إذا تجاوز الملف الحجم المسموح به.

المشكلة الثانية هي المعالجة غير الصحيحة لطلبات multipart أثناء تدفق الجسم. تحاول بعض الخوادم تحميل طلب multipart بالكامل في الذاكرة قبل التحليل، مما يؤدي إلى OutOfMemoryError للملفات الكبيرة. تدعم الخوادم الحديثة (Nginx، Spring Boot، Ktor) التحليل المتدفق لـ multipart، حيث تتم معالجة كل جزء عند وصوله. يجب على المطور التأكد من أن الخادم مهيأ للمعالجة المتدفقة لطلبات multipart.

الفئة الثالثة من المشاكل هي مهلات انتهاء الوقت عند تحميل الملفات الكبيرة. لدى عملاء HTTP إعدادات readTimeout و connectTimeout التي قد تعمل أثناء التحميل الطويل للملفات التي يتجاوز حجمها 50-100 ميجابايت. الحل هو زيادة المهلات لنقاط نهاية التحميل أو استخدام ترميز النقل المجزأ (chunked transfer encoding) داخل multipart. على الأجهزة الجوالة، من المهم أيضًا معالجة انقطاع التحميل وتنفيذ الاستئناف (resume) عند فقدان الاتصال.

أمان Multipart Upload

تحميل الملفات عبر multipart هو أحد أكثر نقاط نهاية تطبيق الويب عرضة للخطر. يمكن للمهاجم تحميل برنامج نصي قابل للتنفيذ عن طريق إعادة تسميته إلى image.jpg. يجب على الخادم التحقق من نوع MIME للملف الذي تم تحميله ليس من الامتداد بل من المحتوى (magic bytes)، والحد من الأنواع المسموح بها وفحص الملفات بمضاد الفيروسات. يُنصح بتخزين الملفات التي تم تحميلها خارج document-root لخادم الويب وتسليمها عبر وحدة تحكم منفصلة مع التحقق من صلاحيات الوصول.

الأسئلة الشائعة

ما الفرق بين multipart/form-data و application/x-www-form-urlencoded؟

multipart/form-data ينقل كل حقل نموذج ككتلة منفصلة برؤوسها الخاصة ويدعم الملفات الثنائية بدون تشفير. application/x-www-form-urlencoded يشفر جميع البيانات في سلسلة متوافقة مع URI (مفتاح=قيمة&مفتاح2=قيمة2) ولا يدعم الملفات مباشرة — يجب تشفيرها بـ base64.

ما هو الحد الأقصى لحجم الملف لـ Multipart Upload؟

بروتوكول HTTP لا يحد من حجم طلب multipart، ولكن في الممارسة العملية يتم تعيين الحدود بواسطة الخادم. Nginx بشكل افتراضي يحدد 1 ميجابايت، Apache — 2 ميجابايت، Spring Boot — 1 ميجابايت. لتحميل الملفات الكبيرة، قم بتكوين client_max_body_size (Nginx) أو spring.servlet.multipart.max-file-size (Spring Boot) إلى القيمة المطلوبة — على سبيل المثال، 100 ميجابايت.

هل يمكن إرسال ملفات متعددة في طلب multipart واحد؟

نعم، يدعم multipart/form-data ملفات متعددة في طلب واحد. يتم إرسال كل ملف كجزء منفصل مع Content-Disposition و Content-Type الخاصين به. تستخدم نماذج HTML السمة multiple للإدخال type="file". في OkHttp، يتم استدعاء addFormDataPart لكل ملف، وفي Alamofire — append لكل ملف.

لماذا نحتاج إلى حد (boundary) في طلب multipart؟

الحد (Boundary) هو سلسلة فريدة تفصل أجزاء الطلب المركب وتسمح للخادم بتحديد أين ينتهي جزء ويبدأ آخر. يتم إنشاؤها بواسطة العميل وتحدد في رأس Content-Type. بدون حد، لن يتمكن الخادم من تحليل طلب متعدد المكونات إلى حقول وملفات فردية.

كيفية التحقق من نوع الملف الذي تم تحميله على الخادم؟

لا تعتمد على امتداد الملف أو Content-Type من الطلب — يمكن للمهاجم تزييفها. تحقق من نوع MIME عبر magic bytes (البايتات الأولى من الملف): مكتبات Apache Tika على Java، libmagic على C/C++، أمر file على Linux، أو الأدوات المضمنة في الأطر — Files.probeContentType() على Java، mimetypes على Python.

الملخص

  • Multipart Upload — آلية لنقل عدة أجزاء غير متجانسة في طلب HTTP واحد مفصولة بحد.
  • multipart/form-data — نوع MIME القياسي لتحميل الملفات عبر نماذج الويب وتطبيقات الجوال، يدعم النقل الثنائي بدون تشفير.
  • كل جزء من الطلب يحتوي على رؤوس Content-Disposition و Content-Type الخاصة به، مما يسمح بإرسال حقول من أنواع مختلفة في طلب واحد.
  • الحد (Boundary) — سلسلة فاصلة فريدة، يتم إنشاؤها تلقائيًا بواسطة العميل، ويجب ألا تظهر في البيانات المنقولة.
  • المزايا — طلب واحد بدلاً من العديد، نقل ثنائي بدون تشفير base64، دعم الملفات من أي حجم (مع تكوين الخادم المناسب).
  • القيود — حدود الحجم على الخادم، مهلات انتهاء الوقت عند تحميل الملفات الكبيرة، خطر OutOfMemoryError بدون معالجة متدفقة.
  • الأمان — تحقق من نوع MIME بمحتوى الملف وليس بالامتداد، خزن الملفات خارج document-root وافحصها بحثًا عن الفيروسات.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا