Multipart Upload w programowaniu webowym: istota, struktura i działanie multipart/form-data

Autor: IT Sectr Opublikowano: 2026-03-10 Czas czytania: 9 min

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 — przesyłanie wielu części danych w jednym żądaniu HTTP z podziałem przez boundary.
  • multipart/form-data — standardowy typ MIME do przesyłania plików z formularzy HTML i aplikacji mobilnych.
  • Boundary — unikalny ciąg znaków rozdzielający części żądania złożonego, generowany automatycznie przez klienty HTTP.
  • Każda część zawiera nagłówki Content-Disposition i Content-Type opisujące nazwę pola i typ pliku.
  • Multipart Upload jest wydajniejszy niż wielokrotne żądania — jeden POST zastępuje N osobnych wywołań serwera.

Czym jest Multipart Upload?

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.

Kiedy stosuje się Multipart Upload

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.

Różnica między multipart a chunked transfer

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.

Jak działa multipart/form-data

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.

Format boundary i jego generowanie

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.

Struktura żądania multipart

Żą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.

ElementPrzykładWymagany
Content-Typemultipart/form-data; boundary=---Bnd123Tak
Separator części---Bnd123Tak (przed każdą częścią)
Content-Dispositionform-data; name="avatar"; filename="photo.jpg"Tak
Content-Type częściimage/jpegDla plików
Treść części[dane binarne obrazu]Tak
Granica końcowa---Bnd123--Tak (koniec żądania)

Przykład żądania multipart

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.

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("Przesłano: ${response.isSuccessful}")
    }
}

Parsowanie odpowiedzi multipart na serwerze

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.

Zalety przesyłania wieloczęściowego

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.

Porównanie multipart z innymi formatami transmisji

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.

Multipart Upload w programowaniu mobilnym

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.

Optymalizacja przesyłania: kompresja i zmiana rozmiaru

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.

Błędy i ograniczenia Multipart Upload

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.

Bezpieczeństwo Multipart Upload

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

Czym różni się multipart/form-data od application/x-www-form-urlencoded?

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.

Jaki jest maksymalny rozmiar pliku dla Multipart Upload?

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.

Czy można przesyłać wiele plików w jednym żądaniu multipart?

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.

Po co jest potrzebny boundary w żądaniu multipart?

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.

Jak sprawdzić typ przesyłanego pliku na serwerze?

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

  • Multipart Upload — mechanizm przesyłania wielu różnorodnych części w jednym żądaniu HTTP z podziałem przez boundary.
  • multipart/form-data — standardowy typ MIME do przesyłania plików przez formularze internetowe i aplikacje mobilne, obsługuje transmisję binarną bez kodowania.
  • Każda część żądania zawiera własne nagłówki Content-Disposition i Content-Type, co umożliwia przesyłanie pól różnych typów w jednym żądaniu.
  • Boundary — unikalny ciąg znaków rozdzielający części, generowany automatycznie przez klienta i nie może występować w przesyłanych danych.
  • Zalety — jedno żądanie zamiast wielu, transmisja binarna bez kodowania base64, obsługa plików dowolnego rozmiaru (przy odpowiedniej konfiguracji serwera).
  • Ograniczenia — limity rozmiaru na serwerze, timeouty przy przesyłaniu dużych plików, ryzyko OutOfMemoryError bez przetwarzania strumieniowego.
  • Bezpieczeństwo — sprawdzaj typ MIME po zawartości pliku, a nie po rozszerzeniu, zapisuj pliki poza document-root i skanuj je antywirusem.

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.

Omów projekt

Przeczytaj również