Multipart Upload е HTTP механизъм, който позволява предаването на няколко разнородни части данни в една заявка, включително текстови полета и двоични файлове. Всяка част е отделена от уникален граничен низ и има собствен 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 не трябва да надвишава 70 символа според RFC 2046. Всяка част се отделя от низа --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 MB нагоре разликата става критична: 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 чрез анотациите @Multipart и @Part. Разработчикът определя типа данни за всяка част, HTTP клиентът автоматично генерира правилните заглавки.
На iOS същите задачи се решават чрез URLSession с персонализиран HTTPBodyStream или чрез Alamofire с multipartFormData. Alamofire предоставя удобния метод upload(multipartFormData:) за изпращане на multipart заявки. И на двете платформи е важно да се вземе предвид размерът на качваните файлове — за големи файлове (над 10-20 MB) се препоръчва използването на фоново качване, за да не се затвори приложението при минимизиране. На Android за това се използва DownloadManager или WorkManager, на iOS — URLSession с background конфигурация.
При качване на файлове в мобилни приложения трябва да се вземе предвид състоянието на мрежата. Connectivity Manager на Android помага да се определи дали е наличен Wi-Fi или мобилни данни и да се избере оптималният момент за качване. За големи файлове като видео се препоръчва отлагане на качването до свързване с Wi-Fi, за да не се изразходва мобилният трафик на потребителя. WorkManager на Android позволява конфигуриране на такива ограничения чрез NetworkType.UNMETERED.
Преди изпращане на файл чрез Multipart Upload, мобилните приложения често компресират и преоразмеряват изображението. JPEG компресия с качество 85% намалява размера на файла 3-5 пъти без забележима загуба на качество за преглед на екрана. Преоразмеряването на изображението до 1920px по дългата страна допълнително намалява размера. На Android за това се използва Bitmap.compress(), на iOS — UIImageJPEGRepresentation с параметър за компресия 0.85. Такава оптимизация ускорява качването и спестява мобилен трафик.
Най-честата грешка при Multipart Upload — превишаване на лимита за размер на заявката на сървъра. По подразбиране Nginx ограничава размера на тялото на заявката до 1 MB (client_max_body_size), а Tomcat — до 2 MB (maxSwallowSize). Ако разработчикът не увеличи тези лимити, сървърът ще върне грешка 413 Request Entity Too Large. Решение — изрично конфигуриране на максималния размер на качване на сървъра и показване на предупреждение на клиента, ако файлът надвишава разрешения размер.
Вторият проблем — неправилна обработка на multipart заявки при стрийминг на тялото. Някои сървъри се опитват да заредят цялата multipart заявка в паметта преди анализ, което води до OutOfMemoryError за големи файлове. Съвременните сървъри (Nginx, Spring Boot, Ktor) поддържат поточен анализ на multipart, при който всяка част се обработва при пристигането си. Разработчикът трябва да се увери, че сървърът е конфигуриран за потокова обработка на multipart заявки.
Третата категория проблеми — тайм-аути при качване на големи файлове. HTTP клиентите имат настройки readTimeout и connectTimeout, които могат да се активират при продължително качване на файл, по-голям от 50-100 MB. Решение — увеличаване на тайм-аутите за крайните точки за качване или използване на chunked transfer encoding вътре в multipart. На мобилни устройства също е важно да се обработи прекъсването на качването и да се имплементира възобновяване (resume) при загуба на връзка.
Качването на файлове чрез multipart е една от най-уязвимите крайни точки на уеб приложението. Нападател може да качи изпълним скрипт, преименувайки го на image.jpg. Сървърът трябва да проверява MIME типа на качения файл не по разширение, а по съдържание (magic bytes), да ограничава разрешените типове и да сканира файловете с антивирус. Препоръчва се качените файлове да се съхраняват извън document-root на уеб сървъра и да се предоставят чрез отделен контролер с проверка на правата за достъп.
Често задавани въпроси
multipart/form-data предава всяко поле на формуляра като отделен блок със собствени заглавки и поддържа двоични файлове без кодиране. application/x-www-form-urlencoded кодира всички данни в низ, съвместим с URI (ключ=стойност&ключ2=стойност2) и не поддържа файлове директно — те трябва да бъдат кодирани в base64.
Протоколът HTTP не ограничава размера на multipart заявката, но на практика лимитите се задават от сървъра. Nginx по подразбиране ограничава до 1 MB, Apache — до 2 MB, Spring Boot — до 1 MB. За качване на големи файлове конфигурирайте client_max_body_size (Nginx) или spring.servlet.multipart.max-file-size (Spring Boot) до желаната стойност — например 100 MB.
Да, 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 създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също