Signatur
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
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)]));
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);
}
});
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);
// Wichtig · Fallstricke
- Listener vor
send()registrieren: Upload-Listener, die erst nachsend()hinzugefügt werden, bekommen möglicherweise keine Events mehr. Sind beim Aufruf vonsend()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/HEADoder beisend()ohne Daten werden keine Upload-Events ausgelöst. lengthComputableprüfen: Ist die Gesamtgröße unbekannt, isttotalgleich0.- Upload-
loadist nicht dasselbe wie „Server fertig“:xhr.upload.onloadmeldet nur, dass die Daten gesendet wurden. Statuscode und Antwort gibt es erst inxhr.onload. - Fetch als Alternative:
fetch()meldet keinen Upload-Fortschritt. Streaming-Request-Bodies (duplex: 'half') werden nur von Chromium-Browsern unterstützt. Für Fortschrittsanzeigen bleibtXMLHttpRequestUploaddie Lösung, die in allen Browsern funktioniert. - In Web Workern verfügbar, in Service Workern nicht (dort gibt es kein
XMLHttpRequest).