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 для email-повідомлень, а потім адаптований для 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 — multer middleware. Сервер отримує доступ до кожного поля форми та кожного завантаженого файлу незалежно, зберігає файл на диск або в хмарне сховище та повертає клієнту 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 через анотації @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 разів без помітної втрати якості для перегляду на екрані. Ресайз зображення до 1920px по більшій стороні додатково скорочує розмір. На 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, коли кожна частина обробляється в міру надходження. Розробник повинен переконатися, що сервер налаштований на потокову обробку (streaming) multipart-запитів.
Третя категорія проблем — тайм-аути при завантаженні великих файлів. HTTP-клієнти мають налаштування readTimeout та connectTimeout, які можуть спрацювати при тривалому завантаженні файлу розміром понад 50-100 МБ. Рішення — збільшити тайм-аути для ендпоінтів завантаження або використовувати 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 МБ, 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 command на Linux, або вбудовані засоби фреймворків — Files.probeContentType() на Java, mimetypes на Python.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також