Start · Sprachen · JavaScript · Referenz · XMLHttpRequestUpload

XMLHttpRequestUpload

Schnittstelle

Stellt den Upload-Vorgang eines <code>XMLHttpRequest</code> dar und liefert Fortschritts-Events für die gesendeten Daten, z. B. für Upload-Fortschrittsbalken.

seit JavaScript Baseline: alle Browser (seit ca. 2012) Kategorie: webapi

Signatur

interface XMLHttpRequestUpload extends XMLHttpRequestEventTarget

Beschreibung

XMLHttpRequestUpload ist ein undurchsichtiges Objekt, das man nicht selbst erzeugen kann. Jede XMLHttpRequest-Instanz hat genau ein solches Objekt, erreichbar über die Eigenschaft xhr.upload. Es steht für den Upload-Teil einer Anfrage, also für das Senden des Anfragekörpers an den Server.

Die Schnittstelle erbt von XMLHttpRequestEventTarget und hat selbst keine eigenen Methoden oder Eigenschaften. Nützlich wird sie durch ihre Events: loadstart, progress, abort, error, load, timeout und loadend. Diese lassen sich per addEventListener() oder über die passenden on…-Eigenschaften (z. B. xhr.upload.onprogress) abonnieren. Das progress-Event ist ein ProgressEvent mit den Eigenschaften loaded, total und lengthComputable.

Typischer Einsatz ist ein Fortschrittsbalken beim Datei-Upload. Die Fetch-API kann den Upload-Fortschritt bis heute nicht zuverlässig melden. Deshalb ist XMLHttpRequest zusammen mit xhr.upload weiterhin der Standardweg, wenn man den Fortschritt anzeigen will. Die Events auf xhr.upload betreffen nur das Senden. Der Download der Antwort wird über die Events direkt auf xhr gemeldet.

Rückgabewert

Typ
XMLHttpRequestUpload
Beschreibung
Wird über XMLHttpRequest.prototype.upload abgerufen. Es ist immer dasselbe Objekt pro Anfrage und kein Konstruktor ist verfügbar.

Beispiele

Upload-Objekt abrufen und Fortschritt protokollieren

const xhr = new XMLHttpRequest();
const upload = xhr.upload; // Instanz von XMLHttpRequestUpload
console.log(upload instanceof XMLHttpRequestUpload);

upload.addEventListener('progress', (e) => {
  if (e.lengthComputable) {
    const prozent = Math.round((e.loaded / e.total) * 100);
    console.log(`Hochgeladen: ${prozent} %`);
  }
});

xhr.open('POST', '/api/upload');
xhr.send(new Blob(['x'.repeat(1_000_000)]));
true Hochgeladen: 12 % Hochgeladen: 57 % Hochgeladen: 100 %

Datei-Upload mit Fortschrittsbalken als Promise

// <input type="file" id="datei"> und <progress id="balken" max="100" value="0"></progress>
const uploadDatei = (datei, onFortschritt) => new Promise((resolve, reject) => {
  const xhr = new XMLHttpRequest();
  const formData = new FormData();
  formData.append('datei', datei);

  // Listener VOR send() registrieren!
  xhr.upload.onprogress = (e) => {
    if (e.lengthComputable) onFortschritt(e.loaded / e.total);
  };
  xhr.upload.onerror = () => reject(new Error('Netzwerkfehler beim Upload'));
  xhr.upload.onabort = () => reject(new Error('Upload abgebrochen'));

  // Serverantwort kommt über xhr selbst
  xhr.onload = () => (xhr.status >= 200 && xhr.status < 300)
    ? resolve(xhr.responseText)
    : reject(new Error(`HTTP ${xhr.status}`));

  xhr.open('POST', '/api/upload');
  xhr.send(formData);
});

const input = document.querySelector('#datei');
const balken = document.querySelector('#balken');

input.addEventListener('change', async () => {
  try {
    const antwort = await uploadDatei(input.files[0], (anteil) => {
      balken.value = Math.round(anteil * 100);
    });
    console.log('Fertig:', antwort);
  } catch (err) {
    console.error(err.message);
  }
});
Fertig: {"ok":true}

Upload abbrechen und Lebenszyklus-Events beobachten

const xhr = new XMLHttpRequest();

['loadstart', 'progress', 'abort', 'loadend'].forEach((typ) => {
  xhr.upload.addEventListener(typ, (e) => console.log(`upload: ${e.type}`));
});

xhr.open('POST', '/api/upload');
xhr.send(new Blob([new Uint8Array(50_000_000)]));

// Nach 200 ms abbrechen, z. B. per Abbrechen-Button
setTimeout(() => xhr.abort(), 200);
upload: loadstart upload: progress upload: abort upload: loadend

// Wichtig · Fallstricke

  • Listener vor send() registrieren: Upload-Listener, die erst nach send() hinzugefügt werden, bekommen möglicherweise keine Events mehr. Sind beim Aufruf von send() keine Upload-Listener registriert, verzichtet der Browser eventuell ganz auf die Upload-Events.
  • CORS-Preflight: Bei Anfragen an eine andere Herkunft (Cross-Origin) löst schon ein registrierter Upload-Listener einen Preflight-Request (OPTIONS) aus. Der Server muss diesen korrekt beantworten, sonst schlägt der Upload fehl.
  • Keine Events ohne Anfragekörper: Bei GET/HEAD oder bei send() ohne Daten werden keine Upload-Events ausgelöst.
  • lengthComputable prüfen: Ist die Gesamtgröße unbekannt, ist total gleich 0.
  • Upload-load ist nicht dasselbe wie „Server fertig“: xhr.upload.onload meldet nur, dass die Daten gesendet wurden. Statuscode und Antwort gibt es erst in xhr.onload.
  • Fetch als Alternative: fetch() meldet keinen Upload-Fortschritt. Streaming-Request-Bodies (duplex: 'half') werden nur von Chromium-Browsern unterstützt. Für Fortschrittsanzeigen bleibt XMLHttpRequestUpload die Lösung, die in allen Browsern funktioniert.
  • In Web Workern verfügbar, in Service Workern nicht (dort gibt es kein XMLHttpRequest).