Was ist multipart/form-data Content-Type? Datei-Uploads, Beispiele

multipart/form-data ist der HTTP-Content-Type für Formulare mit Dateien. Boundary-Aufbau, Senden mit fetch, curl und k6, typische Fehler erklärt.

Was ist multipart/form-data?

multipart/form-data ist der HTTP-Content-Type, mit dem Formulare gesendet werden, die Dateien oder Binärdaten enthalten. Der Request-Body wird in Teile zerlegt, einen je Feld, getrennt durch einen Boundary-String, und jedes Teil trägt eigene Header. Browser verwenden ihn, wenn ein Formular enctype="multipart/form-data" hat. Definiert ist er in RFC 7578.

Es gibt ihn, weil sich rohe Datei-Bytes nicht sicher in den Text key=value&key=value von application/x-www-form-urlencoded packen lassen. Mit Multipart reisen Textfelder und Binärdateien in einer Anfrage.

So funktioniert multipart/form-data

Der Content-Type-Header nennt eine Boundary, einen String, der nirgends im Inhalt vorkommen darf. Jedes Teil beginnt mit -- und der Boundary, der Body endet mit --, der Boundary und noch einmal --.

POST /api/upload HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----example7MA4YW

------example7MA4YW
Content-Disposition: form-data; name="username"

alice
------example7MA4YW
Content-Disposition: form-data; name="avatar"; filename="photo.jpg"
Content-Type: image/jpeg

(binary JPEG bytes)
------example7MA4YW--
  • Jedes Teil braucht einen Header Content-Disposition: form-data mit dem Feldnamen name. Datei-Teile haben zusätzlich einen filename.
  • Der eigene Content-Type eines Teils ist optional. Fehlt er, gilt das Teil als text/plain. Dateien sollten ihren Medientyp nennen, bei Unkenntnis application/octet-stream.
  • Fügen Sie kein Content-Transfer-Encoding hinzu. RFC 7578 sagt, dass Sender es in HTTP nicht verwenden sollen.

multipart/form-data, urlencoded und JSON im Vergleich

  • multipart/form-data verarbeitet Dateien und Binärfelder, mit etwas Mehraufwand durch Boundary und Header je Teil.
  • application/x-www-form-urlencoded ist der Standard für reine Textformulare. Kompakt, aber für Dateien nicht sinnvoll.
  • application/json ist die übliche Wahl für reine Text-Requests an APIs. Dateien gehen damit nicht direkt: Uploads brauchen Multipart, einen separaten Upload-Schritt oder Base64 im JSON (die Daten werden dadurch etwa ein Drittel größer).

Zum größeren Zusammenhang, was ein Request-Body enthalten kann, siehe API-Payloads und JSON.

multipart/form-data aus Code senden

Browser (fetch und FormData)

const fd = new FormData();
fd.append('username', 'alice');
fd.append('avatar', fileInput.files[0]);

await fetch('/api/upload', { method: 'POST', body: fd });

Setzen Sie hier den Header Content-Type nicht selbst. MDN warnt, dass der Browser sonst die von ihm erzeugte Boundary nicht eintragen kann und der Server den Body nicht mehr zerlegen kann.

curl

curl -X POST https://api.example.com/upload \
  -F "username=alice" \
  -F "avatar=@photo.jpg"

-F lässt curl multipart/form-data senden, und das Präfix @ liest den Wert aus einer Datei. Weitere Flags stehen unter curl-Befehl, und eine Anfrage ohne Installation probieren Sie mit dem kostenlosen Tool curl online.

Python (requests)

import requests

requests.post(
    'https://api.example.com/upload',
    data={'username': 'alice'},
    files={'avatar': open('photo.jpg', 'rb')},
)

k6

import http from 'k6/http';

const img = open('./photo.jpg', 'b');

export default function () {
  http.post('https://api.example.com/upload', {
    username: 'alice',
    avatar: http.file(img, 'photo.jpg', 'image/jpeg'),
  });
}

Mit http.file() in einem Body-Objekt lädt k6 eine Datei hoch. Lesen Sie die Datei einmal in der Init-Phase mit open(path, 'b') ein, nicht in der Default-Funktion.

Auswertung auf dem Server

Der Server braucht einen Multipart-Parser: Multer für Express, request.FILES in Django, request.files in Flask, UploadFile in FastAPI, MultipartFile in Spring. Parser unterscheiden sich darin, ob sie Dateien im Speicher puffern oder auf die Platte streamen. Prüfen Sie das, bevor Sie große Uploads erlauben.

Behandeln Sie alles in einem Teil als nicht vertrauenswürdig. RFC 7578 rät Empfängern, einen Dateinamen nicht unverändert zu übernehmen und Verzeichnisangaben darin zu verwerfen. Auch der Content-Type eines Teils ist nur eine Behauptung des Clients; den echten Dateityp ermitteln Sie aus dem Inhalt.

Häufige Fehler

  • 413 Content Too Large. Der Body überschreitet ein Limit von Server oder Proxy. nginx setzt client_max_body_size standardmäßig auf 1m, ein typisches Foto scheitert also, bis Sie den Wert erhöhen.
  • 400 wegen fehlender Boundary. Im Content-Type fehlte der Parameter boundary, oder er passte nicht zum Body. Meist liegt es am von Hand gesetzten Header.
  • Das Dateifeld kommt leer an. Der Feldname in Formular oder Code stimmt nicht mit dem erwarteten Namen überein, oder der Client hat die Boundary überschrieben.
  • Verstümmelte Dateinamen. Nicht-ASCII-Namen brauchen durchgehend UTF-8. RFC 7578 erlaubt Prozentkodierung in Dateinamen und rät hier von der RFC-5987-Form filename* ab.
  • Uploads, die auf langsamen Leitungen hängen. Sehr große Dateien senden Sie besser per signierter URL direkt an einen Objektspeicher oder mit einem fortsetzbaren Protokoll statt über Ihren API-Server.

Multipart-Uploads unter Last testen

  • Verwenden Sie realistische Dateigrößen und echte Dateiinhalte. Winzige Platzhalter verbergen Bandbreiten- und Verarbeitungskosten, und Zufallsbytes lassen sich nicht komprimieren wie Bilder und PDFs.
  • Testen Sie knapp unter und knapp über dem Größenlimit und prüfen Sie auf ein sauberes 413 statt eines 500.
  • Starten Sie Uploads parallel, um zu sehen, ob der Server Dateien im Speicher puffert und ihm der Speicher ausgeht.
  • Testen Sie aus den Regionen Ihrer Nutzer, denn die Upload-Geschwindigkeit hängt stark von der Entfernung ab.

LoadFocus führt JMeter- und k6-Skripte aus 25 Cloud-Regionen aus. Ein Upload-Skript wie das k6-Beispiel oben läuft damit von mehreren Standorten gleichzeitig. Wenn Sie von einem vorhandenen cURL-Befehl mit -F starten möchten, lesen Sie cURL, HAR, Postman oder OpenAPI in einen Lasttest umwandeln (auf Englisch). Wie sich Parameter je nach Body-Typ unterscheiden, steht in How are Parameters Sent in an HTTP POST Request? (auf Englisch). Die Hinweise zum API-Testing gelten auch für Upload-Endpunkte.

Häufige Fragen zu multipart/form-data

Warum steht die Boundary im Content-Type-Header?

Der Parser braucht die Boundary, bevor er den Body lesen kann, und er kann sie nicht in einem Body suchen, den er noch nicht zerlegt hat. Im Header steht sie von Anfang an bereit.

Kann ich multipart/form-data ohne Dateien nutzen?

Ja, es funktioniert auch für reine Textfelder. Für reine Text-Requests sind urlencoded oder JSON leichter, weil kein Aufwand je Teil entsteht.

Was ist der Unterschied zwischen multipart/form-data und multipart/mixed?

multipart/form-data ist für Formularübermittlungen, und jedes Teil trägt eine Content-Disposition mit Feldnamen. multipart/mixed ist der allgemeine Container für E-Mails und andere MIME-Nachrichten.

Wie sende ich JSON und eine Datei zusammen?

Senden Sie zwei Teile in einer Multipart-Anfrage: ein Textteil mit dem JSON (mit eigenem Content-Type application/json) und ein Datei-Teil. Die Datei als Base64 ins JSON einzubetten funktioniert ebenfalls, macht die Daten aber etwa ein Drittel größer.

Warum scheitert mein Upload, wenn ich den Content-Type selbst setze?

Weil dem Wert die Boundary fehlt oder sie nicht zum Body passt. Lassen Sie den Header ungesetzt und überlassen Sie ihn Ihrem HTTP-Client oder Browser.

Wie groß darf ein Upload sein?

HTTP legt keine Grenze fest. Das Limit kommt von Server, Proxy oder Gateway: nginx nutzt standardmäßig 1m und lässt sich mit client_max_body_size erhöhen. Für sehr große Dateien eignen sich signierte URLs zu einem Objektspeicher oder ein fortsetzbarer Upload.

Wie schnell ist Ihre Website?

Steigern Sie ihre Geschwindigkeit und SEO nahtlos mit unserem kostenlosen Geschwindigkeitstest.

Kostenloser Websitespeed-Test

Analysieren Sie die Ladegeschwindigkeit Ihrer Website und verbessern Sie ihre Leistung mit unserem kostenlosen Seitengeschwindigkeits-Checker.