Multipart Upload مکانیزمی HTTP است که امکان ارسال چندین بخش داده ناهمگن را در یک درخواست فراهم میکند، از جمله فیلدهای متنی و فایلهای باینری. هر بخش با یک رشته مرز (boundary) منحصربهفرد جدا شده و هدر Content-Type مخصوص به خود را دارد. بر اساس MDN Web Docs، 2025، multipart/form-data فرمت استاندارد برای آپلود فایلها از طریق فرمهای HTML است و به طور گسترده در برنامههای وب و موبایل برای ارسال تصاویر، اسناد و سایر فایلها به سرور استفاده میشود.
نکات کلیدی
Multipart Upload روشی برای انتقال داده از طریق پروتکل HTTP است که در آن بدنه درخواست از چندین بخش منطقی مجزا تشکیل شده است. هر بخش میتواند دادههای نوع متفاوتی داشته باشد: فیلد متنی فرم، فایل باینری، شیء JSON یا تصویر. همه بخشها در یک درخواست POST بستهبندی میشوند که نیاز به ارسال N فراخوانی HTTP جداگانه را از بین میبرد. Multipart Upload بخش جداییناپذیر فرمهای وب و APIهای آپلود فایل است.
فرمت 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 در همه جاهایی که نیاز به آپلود فایل است استفاده میشود: آواتارها و عکسهای پروفایل در شبکههای اجتماعی، پیوستها در پیامرسانها، اسناد در سیستمهای CRM، تصاویر محصولات در فروشگاههای اینترنتی. در برنامههای موبایل، Multipart Upload برای ارسال فایلهای رسانهای به سرور استفاده میشود — عکسهای دوربین دستگاه، ضبطهای صوتی، قطعات ویدئویی. بر اساس دادههای Cloudflare Research، حدود 15٪ از تمام درخواستهای POST در وب از multipart/form-data استفاده میکنند.
Multipart Upload و Chunked Transfer مکانیزمهای متفاوتی هستند. Multipart درخواست را به بخشهای محتوایی (فیلدها و فایلها) تقسیم میکند، در حالی که Chunked Transfer جریان داده را به قطعاتی برای انتقال بدون دانستن اندازه کل تقسیم میکند. Multipart میتواند در داخل Chunked Transfer منتقل شود: سرور پاسخ multipart را بدون دانستن حجم کامل آن به صورت تکهتکه ارسال میکند. این مکانیزمها با یکدیگر تضاد ندارند و وظایف مختلفی را در سطوح مختلف حل میکنند.
وقتی مرورگر فرمی با ویژگی 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 رشته منحصربهفردی است که نباید در دادههای ارسالی یافت شود. معمولاً با یک پیشوند شروع میشود (مثلاً ----WebKitFormBoundary یا ----Boundary) و شامل کاراکترهای تصادفی است. مرورگرها و کلاینتهای HTTP به طور خودکار boundary تولید میکنند. طول boundary طبق RFC 2046 نباید از 70 کاراکتر تجاوز کند. هر بخش با رشته --boundary\r\n جدا میشود و پایان درخواست با رشته --boundary--\r\n مشخص میشود.
درخواست multipart دارای ساختار دقیقی است که توسط استانداردهای MIME و HTTP تعریف شده است. هدر درخواست Content-Type: multipart/form-data را با پارامتر boundary تنظیم میکند. بدنه درخواست شامل دنبالهای از بخشها است که هر کدام شامل هدرها و بدنه مخصوص خود هستند. هدرهای بخش شامل Content-Disposition (الزامی) و Content-Type (اختیاری — برای فایلها) هستند. وجود یک خط خالی بین هدرهای بخش و دادههای آن الزامی است.
| عنصر | مثال | الزامی |
|---|---|---|
| Content-Type | multipart/form-data; boundary=---Bnd123 | بله |
| جداکننده بخش | ---Bnd123 | بله (قبل از هر بخش) |
| Content-Disposition | form-data; name="avatar"; filename="photo.jpg" | بله |
| Content-Type بخش | image/jpeg | برای فایلها |
| بدنه بخش | [دادههای باینری تصویر] | بله |
| مرز پایانی | ---Bnd123-- | بله (پایان درخواست) |
بیایید یک مثال واقعی از درخواست multipart را بررسی کنیم که یک فیلد متنی و یک فایل تصویر را ارسال میکند. کلاینت هدر Content-Type با boundary منحصربهفرد تشکیل میدهد. بدنه درخواست به ترتیب شامل تمام فیلدهای فرم است. سرور پس از دریافت این بخشها را تجزیه کرده و به توسعهدهنده به هر فیلد به عنوان یک شیء جداگانه دسترسی میدهد. این رویکرد امکان پردازش فرمهای پیچیده با فایلها را در یک فراخوانی HTTP فراهم میکند.
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 توسط فریمورک یا به صورت دستی تجزیه میشود. در Spring Boot، فقط کافی است از @RequestParam("avatar") MultipartFile file استفاده کنید و فریمورک به طور خودکار فایل را از درخواست multipart استخراج میکند. در Ktor روی Kotlin از receiveMultipart() استفاده میشود، در Express.js — از middleware multer. سرور به هر فیلد فرم و هر فایل آپلود شده به صورت مستقل دسترسی پیدا میکند، فایل را روی دیسک یا فضای ابری ذخیره میکند و URL یا شناسه را به کلاینت برمیگرداند.
Multipart Upload چندین مزیت کلیدی نسبت به روشهای جایگزین انتقال داده ارائه میدهد. یک درخواست به جای چندین درخواست — همه فیلدهای فرم و فایلها در یک فراخوانی HTTP منتقل میشوند که بار شبکه و سرور را کاهش میدهد. نیازی به باز کردن N اتصال برای آپلود N فایل نیست — همه چیز در یک POST بستهبندی میشود. این امر به ویژه برای برنامههای موبایل مهم است، جایی که هر اتصال HTTP به معنای تأخیر و مصرف باتری است.
انتقال باینری بدون کدگذاری — برخلاف application/x-www-form-urlencoded که دادههای باینری را در base64 کدگذاری میکند (افزایش حجم 33٪)، multipart/form-data فایلها را به صورت باینری اصلی منتقل میکند. این از نظر اندازه و سرعت کارآمدتر است. برای فایلهای بزرگ از 10 مگابایت به بالا، تفاوت حیاتی میشود: درخواست multipart 30٪ کوچکتر از درخواست URL-encoded با همان فایل خواهد بود.
ساختار دلخواه — multipart امکان ترکیب فیلدهای انواع مختلف را به هر ترتیبی فراهم میکند. فرم میتواند همزمان شامل فیلدهای متنی، چندین فایل، دادههای JSON و فیلدهای مخفی باشد. هر بخش Content-Type مخصوص خود را دارد که امکان ترکیب دادههای متنی و باینری را فراهم میکند. برای مقایسه: کدگذاری base64 33٪ به اندازه اضافه میکند، در حالی که multipart فقط حدود 5-15٪ برای هدرهای سرویس اضافه میکند.
بر اساس مطالعه HTTP Archive، 2025، multipart/form-data در 94٪ موارد آپلود فایل در وب استفاده میشود. جایگزینها — base64 در JSON (4٪) و انتقال مستقیم از طریق WebSocket (2٪). JSON با base64 برای APIهایی که سایر دادهها نیز در JSON هستند مناسب است، اما برای فایلهای بزرگ ناکارآمد است. WebSocket برای زمان واقعی مناسب است اما توسط همه زیرساختهای HTTP پشتیبانی نمیشود. Multipart به دلیل سادگی و کارایی، استاندارد آپلود فایل باقی میماند.
در برنامههای موبایل، Multipart Upload برای ارسال محتوای رسانهای از دستگاههای کاربران استفاده میشود: عکسهای گالری، تصاویر دوربین، ضبطهای صوتی، فایلهای اسناد. در Android روش استاندارد OkHttp با MultipartBody.Builder است که امکان تشکیل آسان درخواستهای multipart را فراهم میکند. Retrofit نیز multipart را از طریق annotationهای @Multipart و @Part پشتیبانی میکند. توسعهدهنده نوع داده را برای هر بخش مشخص میکند، کلاینت HTTP به طور خودکار هدرهای صحیح را تولید میکند.
در iOS همین وظایف از طریق URLSession با HTTPBodyStream سفارشی یا از طریق Alamofire با multipartFormData حل میشوند. Alamofire متد راحت upload(multipartFormData:) را برای ارسال درخواستهای multipart فراهم میکند. در هر دو پلتفرم مهم است که اندازه فایلهای آپلودی در نظر گرفته شود — برای فایلهای بزرگ (بیش از 10-20 مگابایت) توصیه میشود از آپلود پسزمینه استفاده کنید تا برنامه هنگام کوچکسازی بسته نشود. در Android برای این کار از DownloadManager یا WorkManager استفاده میشود، در iOS — از URLSession با background configuration.
در هنگام آپلود فایلها در برنامههای موبایل باید وضعیت شبکه در نظر گرفته شود. 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 — تجاوز از حد اندازه درخواست در سرور است. به طور پیشفرض 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 مگابایت فعال شوند. راه حل — افزایش تایماوتها برای endpointهای آپلود یا استفاده از chunked transfer encoding در داخل multipart. در دستگاههای موبایل همچنین مهم است که قطع شدن آپلود را مدیریت کرده و قابلیت ادامه (resume) را در صورت قطع اتصال پیادهسازی کنید.
آپلود فایلها از طریق multipart یکی از آسیبپذیرترین endpointهای برنامه وب است. مهاجم میتواند یک اسکریپت اجرایی را با تغییر نام آن به image.jpg آپلود کند. سرور باید نوع MIME فایل آپلودی را نه بر اساس پسوند، بلکه بر اساس محتوا (magic bytes) بررسی کند، انواع مجاز را محدود کرده و فایلها را با آنتیویروس اسکن کند. توصیه میشود فایلهای آپلود شده را خارج از document-root وب سرور ذخیره کرده و آنها را از طریق یک کنترلر جداگانه با بررسی مجوز دسترسی ارائه دهید.
سوالات متداول
multipart/form-data هر فیلد فرم را به عنوان یک بلوک جداگانه با هدرهای خودش منتقل میکند و از فایلهای باینری بدون کدگذاری پشتیبانی میکند. application/x-www-form-urlencoded تمام دادهها را به یک رشته سازگار با URI کدگذاری میکند (کلید=مقدار&کلید2=مقدار2) و مستقیماً از فایلها پشتیبانی نمیکند — آنها باید در base64 کدگذاری شوند.
پروتکل HTTP اندازه درخواست multipart را محدود نمیکند، اما در عمل محدودیتها توسط سرور تعیین میشوند. Nginx به طور پیشفرض به 1 مگابایت، Apache — 2 مگابایت، Spring Boot — 1 مگابایت محدود میکند. برای آپلود فایلهای بزرگ، client_max_body_size (Nginx) یا spring.servlet.multipart.max-file-size (Spring Boot) را به مقدار دلخواه — مثلاً 100 مگابایت — تنظیم کنید.
بله، multipart/form-data از چندین فایل در یک درخواست پشتیبانی میکند. هر فایل به عنوان یک بخش جداگانه با Content-Disposition و Content-Type مخصوص خود منتقل میشود. فرمهای HTML از ویژگی multiple برای input type="file" استفاده میکنند. در OkHttp برای این کار addFormDataPart برای هر فایل فراخوانی میشود، در Alamofire — append برای هر فایل.
Boundary رشته منحصربهفردی است که بخشهای درخواست ترکیبی را جدا میکند و به سرور امکان میدهد تعیین کند یک بخش کجا تمام میشود و بخش دیگر کجا شروع میشود. توسط کلاینت تولید میشود و در هدر Content-Type مشخص میشود. بدون boundary سرور نمیتواند درخواست چندبخشی را به فیلدها و فایلهای جداگانه تجزیه کند.
به پسوند فایل یا Content-Type درخواست اعتماد نکنید — مهاجم میتواند آنها را جعل کند. نوع MIME را از طریق magic bytes (بایتهای اول فایل) بررسی کنید: کتابخانههای Apache Tika در Java، libmagic در C/C++، دستور file در Linux، یا ابزارهای داخلی فریمورکها — Files.probeContentType() در Java، mimetypes در Python.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید