Multipart Upload to mechanizm HTTP, który umożliwia przesyłanie wielu różnorodnych części danych w jednym żądaniu, w tym pól tekstowych i plików binarnych. Każda część jest oddzielona unikalnym ciągiem granicznym i posiada własny nagłówek Content-Type. Według MDN Web Docs, 2025, multipart/form-data jest standardowym formatem do przesyłania plików przez formularze HTML i jest szeroko stosowany w aplikacjach internetowych i mobilnych do wysyłania obrazów, dokumentów i innych plików na serwer.
Najważniejsze
Multipart Upload to metoda przesyłania danych za pomocą protokołu HTTP, w której treść żądania składa się z kilku logicznie oddzielonych części. Każda część może zawierać dane różnego typu: pole tekstowe formularza, plik binarny, obiekt JSON lub obraz. Wszystkie części są pakowane w jedno żądanie POST, co zastępuje konieczność wysyłania N osobnych wywołań HTTP. Multipart Upload jest nieodłączną częścią formularzy internetowych i API do przesyłania plików.
Format multipart został zdefiniowany w specyfikacji RFC 2046 jako część standardu MIME dla wiadomości email, a następnie zaadaptowany dla HTTP w RFC 1867. Obecnie w programowaniu webowym używa się prawie wyłącznie multipart/form-data — jednego z podtypów multipart, przeznaczonego dla formularzy zawierających pliki. Inne podtypy — multipart/mixed (do dowolnych załączników) i multipart/byteranges (do częściowego pobierania plików) — są stosowane znacznie rzadziej.
Zasadnicza różnica między multipart a zwykłym application/x-www-form-urlencoded polega na tym, że ten ostatni koduje wszystkie dane w ciąg znaków zgodny z URI i nie obsługuje plików binarnych. Multipart/form-data natomiast przesyła każdy plik w oryginalnej postaci binarnej bez kodowania, co jest wydajniejsze i nie traci dokładności. Rozmiar żądania przy multipart jest tylko o 5-15% większy niż suma rozmiarów plików ze względu na narzut na nagłówki części i granice.
Multipart Upload jest stosowany wszędzie tam, gdzie wymagane jest przesyłanie plików: awatary i zdjęcia profilowe w mediach społecznościowych, załączniki w komunikatorach, dokumenty w systemach CRM, obrazy produktów w sklepach internetowych. W aplikacjach mobilnych Multipart Upload służy do wysyłania plików multimedialnych na serwer — zdjęć z aparatu urządzenia, nagrań głosowych, fragmentów wideo. Według danych Cloudflare Research, około 15% wszystkich żądań POST w sieci używa multipart/form-data.
Multipart Upload i Chunked Transfer to różne mechanizmy. Multipart dzieli żądanie na znaczące części (pola i pliki), a Chunked Transfer dzieli strumień danych na fragmenty do przesyłania bez znajomości całkowitego rozmiaru. Multipart może być przesyłany wewnątrz Chunked Transfer: serwer wysyła odpowiedź multipart w częściach, nie znając jej pełnego rozmiaru. Te mechanizmy nie są ze sobą sprzeczne i rozwiązują różne zadania na różnych poziomach.
Gdy przeglądarka wysyła formularz z atrybutem enctype="multipart/form-data", konstruuje treść żądania w formacie multipart. Każde pole formularza staje się oddzielnym blokiem, oddzielonym od innych ciągiem granicznym (boundary). Granica jest generowana automatycznie i stanowi unikalną sekwencję znaków, która gwarantowanie nie występuje wewnątrz danych. Klient dodaje tę granicę do nagłówka Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX7K.
Każdy blok zaczyna się od --boundary i zawiera nagłówki Content-Disposition z nazwą pola (name) oraz, dla plików, oryginalną nazwą pliku (filename). Po pustej linii następują bezpośrednio dane pola lub zawartość pliku w postaci binarnej. Żądanie kończy się ciągiem --boundary--. Serwer analizuje odebrany strumień: najpierw znajduje granicę, następnie wyodrębnia nagłówki każdej części, określa typ danych i przekazuje je do handlera formularza lub kontrolera API.
Według IETF RFC 7578, multipart/form-data nie wymaga określania charset dla każdej części, ponieważ pola tekstowe są domyślnie w UTF-8, a części binarne zawierają pliki w oryginalnym kodowaniu. Rozmiar pojedynczej części nie jest ograniczony przez protokół — ograniczenia są konfigurowane na poziomie serwera: na przykład w Nginx przez client_max_body_size, w Spring Boot przez spring.servlet.multipart.max-file-size.
Boundary to unikalny ciąg znaków, który nie może występować w przesyłanych danych. Zazwyczaj zaczyna się od prefiksu (np. ----WebKitFormBoundary lub ----Boundary) i zawiera losowe znaki. Przeglądarki i klienty HTTP generują boundary automatycznie. Długość boundary nie powinna przekraczać 70 znaków zgodnie z RFC 2046. Każda część jest oddzielana ciągiem --boundary\r\n, a koniec żądania — ciągiem --boundary--\r\n.
Żądanie multipart ma ścisłą strukturę określoną przez standardy MIME i HTTP. Nagłówek żądania ustawia Content-Type: multipart/form-data z parametrem boundary. Treść żądania składa się z sekwencji części, z których każda zawiera własne nagłówki i treść. Nagłówki części obejmują Content-Disposition (wymagany) i Content-Type (opcjonalny — dla plików). Wymagana jest pusta linia między nagłówkami części a jej danymi.
| Element | Przykład | Wymagany |
|---|---|---|
| Content-Type | multipart/form-data; boundary=---Bnd123 | Tak |
| Separator części | ---Bnd123 | Tak (przed każdą częścią) |
| Content-Disposition | form-data; name="avatar"; filename="photo.jpg" | Tak |
| Content-Type części | image/jpeg | Dla plików |
| Treść części | [dane binarne obrazu] | Tak |
| Granica końcowa | ---Bnd123-- | Tak (koniec żądania) |
Rozważmy rzeczywisty przykład żądania multipart wysyłającego pole tekstowe i plik obrazu. Klient tworzy nagłówek Content-Type z unikalnym boundary. Treść żądania zawiera kolejno wszystkie pola formularza. Serwer po odebraniu analizuje te części i udostępnia programiście dostęp do każdego pola jako osobnego obiektu. Takie podejście umożliwia obsługę złożonych formularzy z plikami w jednym wywołaniu 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("Przesłano: ${response.isSuccessful}")
}
}
Po stronie serwera żądanie multipart jest analizowane przez framework lub ręcznie. W Spring Boot wystarczy adnotacja @RequestParam("avatar") MultipartFile file, a framework automatycznie wyodrębnia plik z żądania multipart. W Ktor na Kotlin używa się receiveMultipart(), w Express.js — middleware multer. Serwer uzyskuje dostęp do każdego pola formularza i każdego przesłanego pliku niezależnie, zapisuje plik na dysku lub w chmurze i zwraca klientowi URL lub identyfikator.
Multipart Upload zapewnia kilka kluczowych zalet w porównaniu z alternatywnymi sposobami przesyłania danych. Jedno żądanie zamiast wielu — wszystkie pola formularza i pliki są przesyłane w jednym wywołaniu HTTP, co zmniejsza obciążenie sieci i serwera. Nie trzeba otwierać N połączeń do przesłania N plików — wszystko jest pakowane w jeden POST. Jest to szczególnie ważne w aplikacjach mobilnych, gdzie każde połączenie HTTP oznacza opóźnienie i zużycie baterii.
Transmisja binarna bez kodowania — w przeciwieństwie do application/x-www-form-urlencoded, gdzie dane binarne są kodowane w base64 (zwiększenie rozmiaru o 33%), multipart/form-data przesyła pliki w oryginalnej postaci binarnej. Jest to wydajniejsze pod względem rozmiaru i szybkości. W przypadku dużych plików o rozmiarze od 10 MB różnica staje się krytyczna: żądanie multipart będzie o 30% mniejsze niż żądanie URL-encoded z tym samym plikiem.
Dowolna struktura — multipart umożliwia łączenie pól różnych typów w dowolnej kolejności. Formularz może zawierać pola tekstowe, wiele plików, dane JSON i pola ukryte jednocześnie. Każda część ma własny Content-Type, co pozwala mieszać dane tekstowe i binarne. Dla porównania: kodowanie base64 dodaje 33% do rozmiaru, a multipart tylko około 5-15% na nagłówki pomocnicze.
Według danych badania HTTP Archive, 2025, multipart/form-data jest używany w 94% przypadków przesyłania plików w sieci. Alternatywy — base64 w JSON (4%) i bezpośrednia transmisja przez WebSocket (2%). JSON z base64 jest wygodny dla API, gdzie wszystkie inne dane również są w JSON, ale nieefektywny dla dużych plików. WebSocket nadaje się do transmisji w czasie rzeczywistym, ale nie jest obsługiwany przez wszystkie infrastruktury HTTP. Multipart pozostaje standardem przesyłania plików dzięki swojej prostocie i wydajności.
W aplikacjach mobilnych Multipart Upload jest używany do wysyłania treści multimedialnych z urządzeń użytkowników: zdjęć z galerii, zdjęć z aparatu, nagrań głosowych, plików dokumentów. Na Android standardowym sposobem jest OkHttp z MultipartBody.Builder, który umożliwia łatwe tworzenie żądań multipart. Retrofit również obsługuje multipart przez adnotacje @Multipart i @Part. Programista określa typ danych dla każdej części, klient HTTP automatycznie tworzy odpowiednie nagłówki.
Na iOS te same zadania są rozwiązywane przez URLSession z niestandardowym HTTPBodyStream lub przez Alamofire z multipartFormData. Alamofire udostępnia wygodną metodę upload(multipartFormData:) do wysyłania żądań multipart. Na obu platformach ważne jest uwzględnienie rozmiaru przesyłanych plików — w przypadku dużych plików (powyżej 10-20 MB) zaleca się korzystanie z przesyłania w tle, aby aplikacja nie zamykała się przy minimalizacji. Na Androidzie służy do tego DownloadManager lub WorkManager, na iOS — URLSession z background configuration.
Podczas przesyłania plików w aplikacjach mobilnych należy uwzględnić stan sieci. Connectivity Manager na Androidzie pomaga określić, czy dostępne jest Wi-Fi czy dane mobilne, i wybrać optymalny moment do przesłania. W przypadku dużych plików, takich jak wideo, zaleca się odłożenie przesyłania do momentu połączenia z Wi-Fi, aby nie zużywać mobilnego transferu użytkownika. WorkManager na Androidzie umożliwia skonfigurowanie takich ograniczeń przez NetworkType.UNMETERED.
Przed wysłaniem pliku przez Multipart Upload aplikacje mobilne często kompresują i zmieniają rozmiar obrazu. Kompresja JPEG z jakością 85% zmniejsza rozmiar pliku 3-5 razy bez zauważalnej utraty jakości przy oglądaniu na ekranie. Zmiana rozmiaru obrazu do 1920px na dłuższym boku dodatkowo zmniejsza rozmiar. Na Androidzie służy do tego Bitmap.compress(), na iOS — UIImageJPEGRepresentation z parametrem kompresji 0.85. Taka optymalizacja przyspiesza przesyłanie i oszczędza transfer mobilny.
Najczęstszym błędem przy Multipart Upload jest przekroczenie limitu rozmiaru żądania na serwerze. Domyślnie Nginx ogranicza rozmiar treści żądania do 1 MB (client_max_body_size), a Tomcat — do 2 MB (maxSwallowSize). Jeśli programista nie zwiększy tych limitów, serwer zwróci błąd 413 Request Entity Too Large. Rozwiązanie — jawnie skonfigurować maksymalny rozmiar przesyłania na serwerze i wyświetlać ostrzeżenie na kliencie, jeśli plik przekracza dozwolony rozmiar.
Drugi problem — nieprawidłowe przetwarzanie żądań multipart podczas strumieniowania treści. Niektóre serwery próbują załadować całe żądanie multipart do pamięci przed analizą, co prowadzi do OutOfMemoryError w przypadku dużych plików. Nowoczesne serwery (Nginx, Spring Boot, Ktor) obsługują strumieniowe przetwarzanie multipart, gdzie każda część jest przetwarzana na bieżąco. Programista powinien upewnić się, że serwer jest skonfigurowany do strumieniowego przetwarzania żądań multipart.
Trzecia kategoria problemów — timeouty przy przesyłaniu dużych plików. Klienty HTTP mają ustawienia readTimeout i connectTimeout, które mogą zadziałać przy długotrwałym przesyłaniu pliku o rozmiarze powyżej 50-100 MB. Rozwiązanie — zwiększyć timeouty dla endpointów przesyłania lub użyć chunked transfer encoding wewnątrz multipart. Na urządzeniach mobilnych ważne jest również obsłużenie przerwania przesyłania i zaimplementowanie wznowienia (resume) w przypadku utraty połączenia.
Przesyłanie plików przez multipart to jeden z najbardziej podatnych na ataki endpointów aplikacji internetowej. Osoba atakująca może przesłać wykonywalny skrypt, zmieniając jego nazwę na image.jpg. Serwer musi sprawdzać typ MIME przesyłanego pliku nie po rozszerzeniu, ale po zawartości (magic bytes), ograniczać dozwolone typy i skanować pliki antywirusem. Zaleca się zapisywanie przesłanych plików poza document-root serwera internetowego i udostępnianie ich przez osobny kontroler z weryfikacją uprawnień dostępu.
Często zadawane pytania
multipart/form-data przesyła każde pole formularza jako osobny blok z własnymi nagłówkami i obsługuje pliki binarne bez kodowania. application/x-www-form-urlencoded koduje wszystkie dane w ciąg znaków zgodny z URI (klucz=wartość&klucz2=wartość2) i nie obsługuje plików bezpośrednio — należy je kodować w base64.
Protokół HTTP nie ogranicza rozmiaru żądania multipart, ale w praktyce limity są ustawiane przez serwer. Nginx domyślnie ogranicza do 1 MB, Apache — do 2 MB, Spring Boot — do 1 MB. Aby przesyłać duże pliki, skonfiguruj client_max_body_size (Nginx) lub spring.servlet.multipart.max-file-size (Spring Boot) do żądanej wartości — na przykład 100 MB.
Tak, multipart/form-data obsługuje wiele plików w jednym żądaniu. Każdy plik jest przesyłany jako osobna część z własnym Content-Disposition i Content-Type. Formularze HTML używają atrybutu multiple dla input type="file". W OkHttp wywołuje się addFormDataPart dla każdego pliku, w Alamofire — append dla każdego pliku.
Boundary to unikalny ciąg znaków, który rozdziela części żądania złożonego i umożliwia serwerowi określenie, gdzie kończy się jedna część, a zaczyna druga. Jest generowany przez klienta i podawany w nagłówku Content-Type. Bez boundary serwer nie będzie w stanie rozdzielić żądania wieloczęściowego na osobne pola i pliki.
Nie polegaj na rozszerzeniu pliku ani Content-Type z żądania — mogą one zostać sfałszowane przez osobę atakującą. Sprawdzaj typ MIME przez magic bytes (pierwsze bajty pliku): biblioteki Apache Tika w Javie, libmagic w C/C++, polecenie file na Linux lub wbudowane narzędzia frameworków — Files.probeContentType() w Javie, mimetypes w Pythonie.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również