Start · Sprachen · JavaScript · Referenz · XMLHttpRequest.upload

XMLHttpRequest.upload

Eigenschaft

Liefert ein <code>XMLHttpRequestUpload</code>-Objekt, über dessen Events sich der Fortschritt beim Hochladen des Request-Bodys verfolgen lässt.

seit JavaScript Baseline: alle Browser (XMLHttpRequest Level 2) Kategorie: webapi

Signatur

XMLHttpRequest.upload

Beschreibung

Die schreibgeschützte Eigenschaft upload gibt ein XMLHttpRequestUpload-Objekt zurück. Es gehört fest zu der jeweiligen XMLHttpRequest-Instanz und ist ein EventTarget, an dem sich Listener für den Upload-Teil einer Anfrage registrieren lassen, also für das Senden des Request-Bodys an den Server.

Folgende Events stehen zur Verfügung (jeweils per addEventListener() oder als on…-Eigenschaft):

  • loadstart: Der Upload beginnt.
  • progress: Wird regelmäßig ausgelöst. Das ProgressEvent enthält loaded, total und lengthComputable.
  • load: Der Upload ist erfolgreich abgeschlossen. Die Server-Antwort ist dann aber noch nicht zwingend angekommen.
  • error, abort, timeout: Der Upload ist fehlgeschlagen, wurde abgebrochen oder hat das Zeitlimit überschritten.
  • loadend: Der Upload ist beendet, egal ob erfolgreich oder nicht.

Typische Einsatzfälle sind Fortschrittsbalken beim Hochladen von Dateien mit FormData oder Blob. Wichtig: Die fetch()-API bietet bis heute keine gleichwertige, breit unterstützte Möglichkeit, den Upload-Fortschritt zu beobachten. Deshalb ist xhr.upload dafür weiterhin das Mittel der Wahl.

Rückgabewert

Typ
XMLHttpRequestUpload
Beschreibung
Das zur Anfrage gehörende XMLHttpRequestUpload-Objekt (ein EventTarget). Bei jedem Zugriff kommt dieselbe Instanz zurück.

Beispiele

Datei-Upload mit Fortschrittsanzeige

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

input.addEventListener('change', () => {
  const datei = input.files[0];
  if (!datei) return;

  const formData = new FormData();
  formData.append('datei', datei);

  const xhr = new XMLHttpRequest();

  // Listener VOR send() registrieren
  xhr.upload.addEventListener('progress', (event) => {
    if (event.lengthComputable) {
      const prozent = Math.round((event.loaded / event.total) * 100);
      balken.value = prozent;
      console.log(`Hochgeladen: ${prozent} %`);
    }
  });

  xhr.upload.addEventListener('load', () => console.log('Upload abgeschlossen'));
  xhr.addEventListener('load', () => console.log('Server-Antwort:', xhr.status));

  xhr.open('POST', '/api/upload');
  xhr.send(formData);
});
Hochgeladen: 12 % Hochgeladen: 57 % Hochgeladen: 100 % Upload abgeschlossen Server-Antwort: 200

Upload als Promise mit Fortschritts-Callback und Abbruch

const uploadMitFortschritt = (url, body, onProgress, signal) =>
  new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    xhr.upload.onprogress = (e) => {
      if (e.lengthComputable) onProgress(e.loaded / e.total);
    };
    xhr.upload.onerror = () => reject(new Error('Netzwerkfehler beim Upload'));
    xhr.upload.onabort = () => reject(new DOMException('Abgebrochen', 'AbortError'));

    xhr.onload = () =>
      xhr.status >= 200 && xhr.status < 300
        ? resolve(xhr.response)
        : reject(new Error(`HTTP ${xhr.status}`));

    signal?.addEventListener('abort', () => xhr.abort());

    xhr.open('POST', url);
    xhr.responseType = 'json';
    xhr.send(body);
  });

// Nutzung
const controller = new AbortController();
const blob = new Blob([new Uint8Array(5_000_000)]);

try {
  const antwort = await uploadMitFortschritt(
    '/api/upload',
    blob,
    (anteil) => console.log(`${(anteil * 100).toFixed(0)} %`),
    controller.signal
  );
  console.log('Fertig:', antwort);
} catch (err) {
  console.error(err.message);
}
20 % 64 % 100 % Fertig: { id: 42 }

Alle Upload-Phasen protokollieren

const xhr = new XMLHttpRequest();
const phasen = ['loadstart', 'progress', 'load', 'error', 'abort', 'timeout', 'loadend'];

for (const typ of phasen) {
  xhr.upload.addEventListener(typ, (e) => {
    console.log(`upload:${e.type}`, e.loaded, '/', e.total);
  });
}

xhr.open('POST', '/api/echo');
xhr.send('Hallo Server!');
upload:loadstart 0 / 13 upload:progress 13 / 13 upload:load 13 / 13 upload:loadend 13 / 13

// Wichtig · Fallstricke

  • Listener vor send() registrieren: Listener, die erst nach send() hinzukommen, verpassen unter Umständen Events oder werden gar nicht ausgelöst.
  • CORS-Preflight: Schon ein einziger Listener auf xhr.upload macht einen Cross-Origin-Request zu einer „nicht einfachen“ Anfrage. Der Browser schickt dann einen OPTIONS-Preflight, und der Server muss darauf korrekt antworten, sonst schlägt die Anfrage fehl.
  • Ohne Body keine Upload-Events: Bei GET/HEAD oder send() ohne Body werden keine Upload-Events ausgelöst.
  • event.lengthComputable immer prüfen. Ist der Wert false, kann total gleich 0 sein.
  • xhr.upload.onload bedeutet nur, dass die Daten gesendet wurden. Ob der Server sie angenommen hat, zeigt erst xhr.onload zusammen mit xhr.status.
  • Die Fortschrittswerte spiegeln den Stand der Netzwerkpuffer des Browsers wider und sind deshalb nicht exakt. Bei kleinen Uploads kommt oft nur ein einziges progress-Event.
  • In Service Workern gibt es XMLHttpRequest nicht. fetch() unterstützt Upload-Fortschritt bisher nicht breit; Streaming-Request-Bodys gibt es nur in Chromium und nur mit HTTP/2+.
  • Die Eigenschaft ist in allen modernen Browsern verfügbar, ab IE 10.