Multipart Upload je HTTP mechanismus, který umožňuje přenášet několik heterogenních částí dat v jediném požadavku, včetně textových polí a binárních souborů. Každá část je oddělena jedinečným hraničním řetězcem a má vlastní hlavičku Content-Type. Podle MDN Web Docs, 2025 je multipart/form-data standardním formátem pro nahrávání souborů přes HTML formuláře a je široce používán ve webových a mobilních aplikacích pro odesílání obrázků, dokumentů a dalších souborů na server.
Hlavní body
Multipart Upload je metoda přenosu dat pomocí protokolu HTTP, při které se tělo požadavku skládá z několika logicky oddělených částí. Každá část může obsahovat data různého typu: textové pole formuláře, binární soubor, JSON objekt nebo obrázek. Všechny části jsou zabaleny do jednoho POST požadavku, což nahrazuje potřebu odesílat N samostatných HTTP volání. Multipart Upload je nedílnou součástí webových formulářů a API pro nahrávání souborů.
Formát multipart byl definován ve specifikaci RFC 2046 jako součást standardu MIME pro e-mailové zprávy a poté adaptován pro HTTP v RFC 1867. Dnes se ve webovém vývoji používá téměř výhradně multipart/form-data — jeden z podtypů multipart, určený pro formuláře obsahující soubory. Další podtypy — multipart/mixed (pro libovolné přílohy) a multipart/byteranges (pro částečné stahování souborů) — se používají mnohem méně často.
Zásadní rozdíl mezi multipart a jednoduchým application/x-www-form-urlencoded spočívá v tom, že druhý jmenovaný kóduje všechna data do řetězce kompatibilního s URI a nepodporuje binární soubory. Multipart/form-data naopak přenáší každý soubor v jeho původní binární podobě bez kódování, což je efektivnější a neztrácí přesnost. Velikost požadavku u multipart je pouze o 5-15% větší než součet velikostí souborů kvůli režii na hlavičky částí a hranice.
Multipart Upload se používá všude tam, kde je vyžadováno nahrávání souborů: avatary a profilové fotografie na sociálních sítích, přílohy v messengerech, dokumenty v CRM systémech, obrázky produktů v internetových obchodech. V mobilních aplikacích se Multipart Upload používá pro odesílání mediálních souborů na server — fotografie z fotoaparátu zařízení, hlasové záznamy, video fragmenty. Podle údajů Cloudflare Research přibližně 15% všech POST požadavků na webu používá multipart/form-data.
Multipart Upload a Chunked Transfer jsou různé mechanismy. Multipart rozděluje požadavek na obsahové části (pole a soubory), zatímco Chunked Transfer rozděluje datový tok na fragmenty pro přenos bez znalosti celkové velikosti. Multipart může být přenášen uvnitř Chunked Transfer: server odesílá multipart odpověď po částech, aniž by znal její celkový objem. Tyto mechanismy si neodporují a řeší různé úkoly na různých úrovních.
Když prohlížeč odesílá formulář s atributem enctype="multipart/form-data", konstruuje tělo požadavku ve formátu multipart. Každé pole formuláře se stává samostatným blokem, odděleným od ostatních hraničním řetězcem (boundary). Hranice je generována automaticky a představuje jedinečnou sekvenci znaků, která se zaručeně nevyskytuje uvnitř dat. Klient přidává tuto hranici do hlavičky Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX7K.
Každý blok začíná --boundary a obsahuje hlavičky Content-Disposition s názvem pole (name) a pro soubory původním názvem souboru (filename). Za prázdným řádkem následují přímo data pole nebo obsah souboru v binární podobě. Požadavek končí řetězcem --boundary--. Server analyzuje přijatý tok: nejprve najde hranici, poté extrahuje hlavičky každé části, určí typ dat a předá je handleru formuláře nebo API kontroléru.
Podle IETF RFC 7578 multipart/form-data nevyžaduje uvedení charset pro každou část, protože textová pole jsou považována za UTF-8 a binární části obsahují soubory v původním kódování. Velikost jedné části není protokolem omezena — omezení se konfigurují na úrovni serveru: například v Nginx přes client_max_body_size, v Spring Boot přes spring.servlet.multipart.max-file-size.
Boundary je jedinečný řetězec, který se nesmí vyskytovat v přenášených datech. Obvykle začíná prefixem (např. ----WebKitFormBoundary nebo ----Boundary) a obsahuje náhodné znaky. Prohlížeče a HTTP klienti generují boundary automaticky. Délka boundary by podle RFC 2046 neměla přesáhnout 70 znaků. Každá část je oddělena řetězcem --boundary\r\n a konec požadavku řetězcem --boundary--\r\n.
Multipart požadavek má přísnou strukturu definovanou standardy MIME a HTTP. Hlavička požadavku nastavuje Content-Type: multipart/form-data s parametrem boundary. Tělo požadavku se skládá z posloupnosti částí, z nichž každá obsahuje vlastní hlavičky a tělo. Hlavičky části zahrnují Content-Disposition (povinný) a Content-Type (volitelný — pro soubory). Povinná je přítomnost prázdného řádku mezi hlavičkami části a jejími daty.
| Prvek | Příklad | Povinnost |
|---|---|---|
| Content-Type | multipart/form-data; boundary=---Bnd123 | Ano |
| Oddělovač části | ---Bnd123 | Ano (před každou částí) |
| Content-Disposition | form-data; name="avatar"; filename="photo.jpg" | Ano |
| Content-Type části | image/jpeg | Pro soubory |
| Tělo části | [binární data obrázku] | Ano |
| Závěrečná hranice | ---Bnd123-- | Ano (konec požadavku) |
Podívejme se na reálný příklad multipart požadavku odesílajícího textové pole a soubor obrázku. Klient vytvoří hlavičku Content-Type s jedinečným boundary. Tělo požadavku postupně obsahuje všechna pole formuláře. Server při příjmu analyzuje tyto části a poskytuje vývojáři přístup ke každému poli jako samostatnému objektu. Tento přístup umožňuje zpracovávat složité formuláře se soubory v jednom HTTP volání.
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("Nahráno: ${response.isSuccessful}")
}
}
Na straně serveru je multipart požadavek analyzován frameworkem nebo ručně. V Spring Boot stačí anotace @RequestParam("avatar") MultipartFile file a framework automaticky extrahuje soubor z multipart požadavku. V Ktor na Kotlin se používá receiveMultipart(), v Express.js — middleware multer. Server získává přístup ke každému poli formuláře a každému nahranému souboru nezávisle, ukládá soubor na disk nebo do cloudového úložiště a vrací klientovi URL nebo identifikátor.
Multipart Upload poskytuje několik klíčových výhod oproti alternativním způsobům přenosu dat. Jeden požadavek místo mnoha — všechna pole formuláře a soubory jsou přenášeny v jednom HTTP volání, což snižuje zatížení sítě a serveru. Není třeba otevírat N spojení pro nahrání N souborů — vše je zabaleno do jednoho POST. To je důležité zejména pro mobilní aplikace, kde každé HTTP spojení znamená zpoždění a spotřebu baterie.
Binární přenos bez kódování — na rozdíl od application/x-www-form-urlencoded, kde jsou binární data kódována do base64 (zvýšení velikosti o 33%), multipart/form-data přenáší soubory v původní binární podobě. To je efektivnější z hlediska velikosti a rychlosti. U velkých souborů od 10 MB výše se rozdíl stává kritickým: multipart požadavek bude o 30% menší než URL-encoded požadavek se stejným souborem.
Libovolná struktura — multipart umožňuje kombinovat pole různých typů v libovolném pořadí. Formulář může současně obsahovat textová pole, několik souborů, JSON data a skrytá pole. Každá část má vlastní Content-Type, což umožňuje míchat textová a binární data. Pro srovnání: kódování base64 přidává 33% k velikosti, zatímco multipart pouze asi 5-15% na servisní hlavičky.
Podle studie HTTP Archive, 2025 se multipart/form-data používá v 94% případů nahrávání souborů na webu. Alternativy — base64 v JSON (4%) a přímý přenos přes WebSocket (2%). JSON s base64 je vhodný pro API, kde jsou všechna ostatní data také v JSON, ale neefektivní pro velké soubory. WebSocket je vhodný pro real-time, ale není podporován všemi HTTP infrastrukturami. Multipart zůstává standardem pro nahrávání souborů díky své jednoduchosti a efektivitě.
V mobilních aplikacích se Multipart Upload používá pro odesílání mediálního obsahu z uživatelských zařízení: fotografie z galerie, snímky z fotoaparátu, hlasové záznamy, soubory dokumentů. Na Androidu je standardním způsobem OkHttp s MultipartBody.Builder, který umožňuje snadné vytváření multipart požadavků. Retrofit také podporuje multipart přes anotace @Multipart a @Part. Vývojář určuje typ dat pro každou část, HTTP klient automaticky generuje správné hlavičky.
Na iOS jsou stejné úkoly řešeny přes URLSession s vlastním HTTPBodyStream nebo přes Alamofire s multipartFormData. Alamofire poskytuje pohodlnou metodu upload(multipartFormData:) pro odesílání multipart požadavků. Na obou platformách je důležité zohlednit velikost nahrávaných souborů — u velkých souborů (nad 10-20 MB) se doporučuje použít nahrávání na pozadí, aby se aplikace při minimalizaci neukončila. Na Androidu se k tomu používá DownloadManager nebo WorkManager, na iOS — URLSession s background konfigurací.
Při nahrávání souborů v mobilních aplikacích je třeba zohlednit stav sítě. Connectivity Manager na Androidu pomáhá určit, zda je dostupné Wi-Fi nebo mobilní data, a vybrat optimální okamžik pro nahrání. U velkých souborů, jako jsou videa, se doporučuje odložit nahrávání do připojení k Wi-Fi, aby se nevyužívala mobilní data uživatele. WorkManager na Androidu umožňuje konfigurovat taková omezení přes NetworkType.UNMETERED.
Před odesláním souboru přes Multipart Upload mobilní aplikace často komprimují a mění velikost obrázku. Komprese JPEG s kvalitou 85% snižuje velikost souboru 3-5krát bez znatelné ztráty kvality pro zobrazení na obrazovce. Změna velikosti obrázku na 1920px na delší straně dále snižuje velikost. Na Androidu se k tomu používá Bitmap.compress(), na iOS — UIImageJPEGRepresentation s parametrem komprese 0.85. Taková optimalizace urychluje nahrávání a šetří mobilní data.
Nejčastější chybou při Multipart Upload je překročení limitu velikosti požadavku na serveru. Ve výchozím nastavení Nginx omezuje velikost těla požadavku na 1 MB (client_max_body_size) a Tomcat na 2 MB (maxSwallowSize). Pokud vývojář tyto limity nezvýší, server vrátí chybu 413 Request Entity Too Large. Řešením je explicitně nakonfigurovat maximální velikost nahrávání na serveru a zobrazit upozornění na klientovi, pokud soubor překračuje povolenou velikost.
Druhý problém — nesprávné zpracování multipart požadavků při streamování těla. Některé servery se pokoušejí načíst celý multipart požadavek do paměti před analýzou, což vede k OutOfMemoryError u velkých souborů. Moderní servery (Nginx, Spring Boot, Ktor) podporují streamovací analýzu multipart, kdy je každá část zpracovávána postupně. Vývojář by se měl ujistit, že je server nakonfigurován pro streamovací zpracování multipart požadavků.
Třetí kategorie problémů — timeouty při nahrávání velkých souborů. HTTP klienti mají nastavení readTimeout a connectTimeout, která se mohou aktivovat při dlouhodobém nahrávání souboru většího než 50-100 MB. Řešením je zvýšit timeouty pro koncové body nahrávání nebo použít chunked transfer encoding uvnitř multipart. Na mobilních zařízeních je také důležité zpracovat přerušení nahrávání a implementovat obnovení (resume) při ztrátě spojení.
Nahrávání souborů přes multipart je jedním z nejzranitelnějších koncových bodů webové aplikace. Útočník může nahrát spustitelný skript přejmenováním na image.jpg. Server musí kontrolovat MIME typ nahraného souboru nikoli podle přípony, ale podle obsahu (magic bytes), omezovat povolené typy a skenovat soubory antivirem. Doporučuje se ukládat nahrané soubory mimo document-root webového serveru a poskytovat je přes samostatný kontrolér s ověřením přístupových práv.
Často kladené otázky
multipart/form-data přenáší každé pole formuláře jako samostatný blok s vlastními hlavičkami a podporuje binární soubory bez kódování. application/x-www-form-urlencoded kóduje všechna data do řetězce kompatibilního s URI (klíč=hodnota&klíč2=hodnota2) a nepodporuje soubory přímo — ty musí být zakódovány do base64.
Protokol HTTP neomezuje velikost multipart požadavku, ale v praxi jsou limity nastaveny serverem. Nginx ve výchozím nastavení omezuje na 1 MB, Apache — na 2 MB, Spring Boot — na 1 MB. Pro nahrávání velkých souborů nakonfigurujte client_max_body_size (Nginx) nebo spring.servlet.multipart.max-file-size (Spring Boot) na požadovanou hodnotu — například 100 MB.
Ano, multipart/form-data podporuje více souborů v jednom požadavku. Každý soubor je přenášen jako samostatná část s vlastním Content-Disposition a Content-Type. HTML formuláře používají atribut multiple pro input type="file". V OkHttp se pro každý soubor volá addFormDataPart, v Alamofire — append pro každý soubor.
Boundary je jedinečný řetězec, který odděluje části složeného požadavku a umožňuje serveru určit, kde jedna část končí a druhá začíná. Je generován klientem a uveden v hlavičce Content-Type. Bez boundary by server nemohl rozdělit vícesložkový požadavek na jednotlivá pole a soubory.
Nespoléhejte se na příponu souboru nebo Content-Type z požadavku — útočník je může zfalšovat. Zkontrolujte typ MIME pomocí magic bytes (první bajty souboru): knihovny Apache Tika v Javě, libmagic v C/C++, příkaz file v Linuxu, nebo vestavěné nástroje frameworků — Files.probeContentType() v Javě, mimetypes v Pythonu.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také