Multipart Upload è un meccanismo HTTP che permette di trasferire più parti eterogenee di dati in una singola richiesta, inclusi campi di testo e file binari. Ogni parte è separata da una stringa di confine unica e ha il proprio header Content-Type. Secondo MDN Web Docs, 2025, multipart/form-data è il formato standard per il caricamento di file tramite moduli HTML ed è ampiamente utilizzato in applicazioni web e mobili per inviare immagini, documenti e altri file al server.
Punti chiave
Multipart Upload è un metodo di trasferimento dati tramite il protocollo HTTP in cui il corpo della richiesta è costituito da più parti logicamente separate. Ogni parte può contenere dati di tipo diverso: un campo di modulo di testo, un file binario, un oggetto JSON o un'immagine. Tutte le parti sono impacchettate in un'unica richiesta POST, eliminando la necessità di inviare N chiamate HTTP separate. Multipart Upload è una parte essenziale dei moduli web e delle API di caricamento file.
Il formato multipart è stato definito nella specifica RFC 2046 come parte dello standard MIME per i messaggi email, e successivamente adattato per HTTP nella RFC 1867. Oggi, lo sviluppo web utilizza quasi esclusivamente multipart/form-data — uno dei sottotipi multipart progettati per moduli contenenti file. Altri sottotipi — multipart/mixed (per allegati arbitrari) e multipart/byteranges (per download parziali di file) — sono usati molto meno frequentemente.
La differenza fondamentale tra multipart e application/x-www-form-urlencoded è che quest'ultimo codifica tutti i dati in una stringa compatibile con URI e non supporta file binari. Multipart/form-data, invece, trasmette ogni file nella sua forma binaria originale senza codifica, il che è più efficiente e non perde precisione. La dimensione della richiesta con multipart è sempre del 5-15% maggiore della somma delle dimensioni dei file a causa dell'overhead degli header delle parti e dei confini.
Multipart Upload è utilizzato ovunque sia richiesto il caricamento di file: avatar e foto del profilo nei social network, allegati nei messenger, documenti nei sistemi CRM, immagini di prodotti nei negozi online. Nelle applicazioni mobili, Multipart Upload è utilizzato per inviare file multimediali al server — foto dalla fotocamera del dispositivo, registrazioni vocali, clip video. Secondo Cloudflare Research, circa il 15% di tutte le richieste POST sul web utilizza multipart/form-data.
Multipart Upload e Chunked Transfer sono meccanismi diversi. Multipart divide una richiesta in parti significative (campi e file), mentre Chunked Transfer divide un flusso di dati in frammenti per la trasmissione senza conoscere la dimensione totale. Multipart può essere trasmesso all'interno di Chunked Transfer: il server invia una risposta multipart in parti senza conoscerne la dimensione completa. Questi meccanismi non entrano in conflitto e risolvono problemi diversi a livelli diversi.
Quando un browser invia un modulo con l'attributo enctype="multipart/form-data", costruisce il corpo della richiesta in formato multipart. Ogni campo del modulo diventa un blocco separato, separato dagli altri da una stringa di confine (boundary). Il confine viene generato automaticamente ed è una sequenza unica di caratteri che garantisce di non apparire all'interno dei dati. Il client aggiunge questo confine all'header Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX7K.
Ogni blocco inizia con --boundary e contiene header Content-Disposition con il nome del campo (name) e, per i file, il nome file originale (filename). Dopo una riga vuota seguono i dati del campo o il contenuto del file in forma binaria. La richiesta termina con la stringa --boundary--. Il server analizza il flusso ricevuto: prima trova il confine, poi estrae gli header di ogni parte, determina il tipo di dati e li passa al gestore del modulo o al controller API.
Secondo IETF RFC 7578, multipart/form-data non richiede di specificare un charset per ogni parte, poiché i campi di testo sono considerati in UTF-8 e le parti binarie contengono file nella loro codifica originale. La dimensione di una parte non è limitata dal protocollo — i limiti sono impostati a livello di server: ad esempio, in Nginx tramite client_max_body_size, in Spring Boot tramite spring.servlet.multipart.max-file-size.
Boundary è una stringa unica che non deve apparire nei dati trasmessi. Di solito inizia con un prefisso (ad esempio, ----WebKitFormBoundary o ----Boundary) e contiene caratteri casuali. I browser e i client HTTP generano automaticamente il boundary. La lunghezza del boundary non deve superare i 70 caratteri secondo RFC 2046. Ogni parte è separata dalla stringa --boundary\r\n, e la fine della richiesta è contrassegnata da --boundary--\r\n.
Una richiesta multipart ha una struttura rigorosa definita dagli standard MIME e HTTP. L'header della richiesta imposta Content-Type: multipart/form-data con un parametro boundary. Il corpo della richiesta consiste in una sequenza di parti, ciascuna contenente i propri header e corpo. Gli header della parte includono Content-Disposition (obbligatorio) e Content-Type (opzionale — per i file). Una riga vuota tra gli header della parte e i suoi dati è obbligatoria.
| Elemento | Esempio | Obbligatorietà |
|---|---|---|
| Content-Type | multipart/form-data; boundary=---Bnd123 | Sì |
| Separatore di parte | ---Bnd123 | Sì (prima di ogni parte) |
| Content-Disposition | form-data; name="avatar"; filename="photo.jpg" | Sì |
| Content-Type di parte | image/jpeg | Per file |
| Corpo della parte | [dati binari dell'immagine] | Sì |
| Confine finale | ---Bnd123-- | Sì (fine richiesta) |
Consideriamo un esempio reale di una richiesta multipart che invia un campo di testo e un file immagine. Il client forma l'header Content-Type con un boundary unico. Il corpo della richiesta contiene sequenzialmente tutti i campi del modulo. Alla ricezione, il server analizza queste parti e fornisce allo sviluppatore l'accesso a ciascun campo come oggetto separato. Questo approccio consente di elaborare moduli complessi con file in una singola chiamata 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("Caricato: ${response.isSuccessful}")
}
}
Sul lato server, la richiesta multipart viene analizzata dal framework o manualmente. In Spring Boot, basta l'annotazione @RequestParam("avatar") MultipartFile file e il framework estrae automaticamente il file dalla richiesta multipart. In Ktor su Kotlin si usa receiveMultipart(), in Express.js — il middleware multer. Il server ottiene l'accesso a ciascun campo del modulo e a ciascun file caricato indipendentemente, salva il file su disco o nel cloud storage e restituisce un URL o un identificatore al client.
Multipart Upload offre diversi vantaggi chiave rispetto ai metodi alternativi di trasferimento dati. Una richiesta invece di molte — tutti i campi del modulo e i file vengono trasmessi in una singola chiamata HTTP, riducendo il carico sulla rete e sul server. Non è necessario aprire N connessioni per caricare N file — tutto è impacchettato in un unico POST. Questo è particolarmente importante per le applicazioni mobili, dove ogni connessione HTTP significa latenza e consumo della batteria.
Trasferimento binario senza codifica — a differenza di application/x-www-form-urlencoded, dove i dati binari vengono codificati in base64 (aumento della dimensione del 33%), multipart/form-data trasmette i file nella loro forma binaria originale. Questo è più efficiente in termini di dimensioni e velocità. Per file grandi oltre 10 MB, la differenza diventa critica: una richiesta multipart sarà del 30% più piccola di una richiesta codificata in URL con lo stesso file.
Struttura arbitraria — multipart consente di combinare campi di tipo diverso in qualsiasi ordine. Un modulo può contenere contemporaneamente campi di testo, più file, dati JSON e campi nascosti. Ogni parte ha il proprio Content-Type, consentendo di mescolare dati di testo e binari. Per confronto: la codifica base64 aggiunge il 33% alla dimensione, mentre multipart aggiunge solo circa il 5-15% per gli header di servizio.
Secondo la ricerca di HTTP Archive, 2025, multipart/form-data è utilizzato nel 94% dei casi di caricamento file sul web. Alternative — base64 in JSON (4%) e trasferimento diretto tramite WebSocket (2%). JSON con base64 è comodo per API dove tutti gli altri dati sono anch'essi in JSON, ma inefficiente per file grandi. WebSocket è adatto per dati in tempo reale ma non è supportato da tutte le infrastrutture HTTP. Multipart rimane lo standard per il caricamento di file grazie alla sua semplicità ed efficienza.
Nelle applicazioni mobili, Multipart Upload viene utilizzato per inviare contenuti multimediali dai dispositivi degli utenti: foto dalla galleria, scatti dalla fotocamera, registrazioni vocali, file di documenti. Su Android, l'approccio standard è OkHttp con MultipartBody.Builder, che consente di formare facilmente richieste multipart. Retrofit supporta anche multipart tramite le annotazioni @Multipart e @Part. Lo sviluppatore specifica il tipo di dati per ogni parte e il client HTTP genera automaticamente gli header corretti.
Su iOS, gli stessi compiti vengono risolti con URLSession con un HTTPBodyStream personalizzato o tramite Alamofire con multipartFormData. Alamofire fornisce un metodo conveniente upload(multipartFormData:) per inviare richieste multipart. Su entrambe le piattaforme, è importante considerare la dimensione dei file caricati — per file grandi (oltre 10-20 MB), si consiglia di utilizzare il caricamento in background in modo che l'applicazione non venga terminata durante la minimizzazione. Su Android, questo viene fatto tramite DownloadManager o WorkManager; su iOS, tramite URLSession con configurazione in background.
Durante il caricamento di file in applicazioni mobili, è necessario considerare lo stato della rete. Connectivity Manager su Android aiuta a determinare se Wi-Fi o dati mobili sono disponibili e scegliere il momento ottimale per il caricamento. Per file grandi come video, si consiglia di posticipare il caricamento fino alla connessione al Wi-Fi per non consumare i dati mobili dell'utente. WorkManager su Android consente di impostare tali vincoli tramite NetworkType.UNMETERED.
Prima di inviare un file tramite Multipart Upload, le applicazioni mobili spesso comprimono e ridimensionano l'immagine. La compressione JPEG con qualità all'85% riduce la dimensione del file di 3-5 volte senza perdita di qualità apprezzabile per la visualizzazione sullo schermo. Il ridimensionamento dell'immagine a 1920px sul lato più lungo riduce ulteriormente la dimensione. Su Android, si utilizza Bitmap.compress(); su iOS, UIImageJPEGRepresentation con parametro di compressione 0,85. Tale ottimizzazione accelera il caricamento e risparmia dati mobili.
L'errore più comune in Multipart Upload è il superamento del limite di dimensione della richiesta sul server. Per impostazione predefinita, Nginx limita la dimensione del corpo della richiesta a 1 MB (client_max_body_size) e Tomcat a 2 MB (maxSwallowSize). Se lo sviluppatore non aumenta questi limiti, il server restituisce un errore 413 Request Entity Too Large. La soluzione è configurare esplicitamente la dimensione massima di caricamento sul server e mostrare un avviso sul client se il file supera la dimensione consentita.
Il secondo problema è la gestione errata delle richieste multipart durante lo streaming del corpo. Alcuni server cercano di caricare l'intera richiesta multipart in memoria prima di analizzarla, causando OutOfMemoryError per file grandi. I server moderni (Nginx, Spring Boot, Ktor) supportano l'analisi in streaming di multipart, dove ogni parte viene elaborata man mano che arriva. Lo sviluppatore deve assicurarsi che il server sia configurato per l'elaborazione in streaming delle richieste multipart.
La terza categoria di problemi sono i timeout durante il caricamento di file grandi. I client HTTP hanno impostazioni di readTimeout e connectTimeout che possono scattare durante caricamenti lunghi di file superiori a 50-100 MB. La soluzione è aumentare i timeout per gli endpoint di caricamento o utilizzare la codifica di trasferimento chunked all'interno di multipart. Su dispositivi mobili, è anche importante gestire l'interruzione del caricamento e implementare la ripresa (resume) in caso di perdita di connessione.
Il caricamento di file tramite multipart è uno degli endpoint più vulnerabili di un'applicazione web. Un attaccante può caricare uno script eseguibile rinominandolo in image.jpg. Il server deve verificare il tipo MIME del file caricato non dall'estensione ma dal contenuto (magic bytes), limitare i tipi consentiti e scansionare i file con un antivirus. Si consiglia di archiviare i file caricati al di fuori del document-root del server web e servirli tramite un controller separato con verifica dei permessi di accesso.
Domande frequenti
multipart/form-data trasmette ogni campo del modulo come un blocco separato con i propri header e supporta file binari senza codifica. application/x-www-form-urlencoded codifica tutti i dati in una stringa compatibile con URI (chiave=valore&chiave2=valore2) e non supporta i file direttamente — devono essere codificati in base64.
Il protocollo HTTP non limita la dimensione di una richiesta multipart, ma in pratica i limiti sono impostati dal server. Nginx per impostazione predefinita limita a 1 MB, Apache a 2 MB, Spring Boot a 1 MB. Per caricare file grandi, configurare client_max_body_size (Nginx) o spring.servlet.multipart.max-file-size (Spring Boot) al valore desiderato — ad esempio, 100 MB.
Sì, multipart/form-data supporta più file in una singola richiesta. Ogni file viene trasmesso come una parte separata con il proprio Content-Disposition e Content-Type. I moduli HTML usano l'attributo multiple per input type="file". In OkHttp, addFormDataPart viene chiamato per ogni file; in Alamofire, append viene chiamato per ogni file.
Boundary è una stringa unica che separa le parti di una richiesta composta e consente al server di determinare dove una parte finisce e un'altra inizia. Viene generato dal client e specificato nell'header Content-Type. Senza boundary, il server non può analizzare una richiesta multicomponente in campi e file individuali.
Non affidarti all'estensione del file o al Content-Type della richiesta — un attaccante può falsificarli. Verifica il tipo MIME tramite magic bytes (i primi byte del file): Apache Tika su Java, libmagic su C/C++, il comando file su Linux, o strumenti integrati del framework — Files.probeContentType() su Java, mimetypes su Python.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche