Javascript XMLHttp-Request – Dateiupload

Datei Upload : Mehrere Videos, Musik, Textdateien, Bilder mit Javascript auf den Server laden

Der XMLHttp-Request ist ein Multitalent: Er führt ein Update der Seite aus, holt Bilder und Daten vom Server und schickt Daten zum Server ohne die Seite neu zu laden. Mit Ajax 1 stand nur ein Event zur Verfügung: onreadystatechange. Ajax 2 beschert uns ein neues Event-Modell mit neuen Events, die eine detailliertere Abfrage der Vorgänge und des Zustands erlauben.

Um Dateien vom lokalen Rechner auf den Server zu laden, reicht schon ein einfaches HTML-Formular und PHP. Mit HTML5 ist das Attribut multiple hinzugekommen, mit dem mehrere Daten ausgewählt und hochgeladen werden.

<form class="fileupload" action="upload.php" method="post">
    <input type="file" id="photos" name="photos[]" multiple>
    <button type="submit" id="upload-btn">Laden</button>
</form>

<div class="filelist"></div>
<div id="result"></div>

Das einfache HTML-Formular würde zusammen mit der PHP-Anwendung schon ausreichen, um Dateien auf den Server zu laden. Allerdings läuft der Prozess dann kommentarlos ab und der Benutzer würde weder über Fehler noch über Erfolg der Aktion unterrichtet und keine Vorschaubilder sehen. Erst etwas Javascript informiert den Benutzer über den Ablauf seines Uploads.

Dass die Thumbnails für die geladenen Bilder (zumindest bei kleinen Bildern) hier so schnell erscheinen, liegt natürlich daran, dass sie noch nicht auf den Server geladen sind, sondern noch im Speicher des Browsers liegen. Wenn große Bilddateien mit mehreren Megabyte ausgewählt werden, braucht der Browser sichtbar Zeit, um die Thumbnails zu erstellen.

Der tatsächliche Upload auf den Server beginnt, wenn die Formulardaten mit dem "Laden"-Button abgeschickt wurden.

Die Result-Zeile macht es sich einfach und zeigt nur die bloße JSON-Response der Anwendung auf dem Server.

Datei-Upload mit Javascript

AJAX 2 hat eine bessere Kontrolle des Datei-Uploads mitgebracht, HTML spendiert das progress-Tag, das mit Javascript in Gang gesetzt wird.

Mit den modernen Browsern – selbst IE10 – braucht der Datei-Upload auch kein jQuery mehr. Wenn IE10 allerdings noch unterstützt werden muss, dann sollten Javascript-Variablen noch mit var anstelle von let deklariert werden.

Die drei Variablen erfassen das Formular mit seinen <form> <input> und <button>-Elementen:

let form = document.querySelector('.fileupload');
let fileSelect = document.getElementById('photos');
let uploadButton = document.getElementById('upload-btn');

// Platzhalter für die Anzeige der Vorschaubilder

Als nächstes braucht das Script einen Event Listener, der auf das onsubmit-Event des Formulars wartet.

form.onsubmit = function(event) {
  event.preventDefault();

  // Den Text des Buttons als visuelles Signal ändern
  uploadButton.innerHTML = 'Uploading ...';

  // Hierhin kommt der Rest des Scripts
}

uploadButton.innerHTML = 'Uploading ...' ändert den Button-Text als Hilfestellung und Information für den Benutzer.

Dateien für den Upload vorbereiten

Der Event Listener startet mit dem Aufruf event.preventDefault(), also dem Objekt, auf dem das Submit-Event abgefangen wurde. Das verhindert, dass der Browser das Formular absendet. Der Ablauf wird jetzt von AJAX gesteuert.

// Die ausgeählten Dateien aus dem input-Element laden
let files = fileSelect.files;

Holt die gewählten Dateien aus dem Input-Element und speichert sie in der Variablen files.

// Ein FormData Objekt erzeugen.
let formData = new FormData();

Legt ein neues FormData-Objekt an. Mit FormData werden die key/value-Paare für den AJAX-Request konstruiert.

AJAX-Request zusammenstellen

Die Zusammenstellung des Requests ist der nächste Schritt. Das passiert in einem Loop über alle Dateien, in dem die Dateien in das gerade erzeugte formData-Objekt eingefügt werden. An dieser Stelle kann auch bereits geprüft werden, ob alle Dateien vom richtigen Typ sind.

// Alle ausgewählten Dateien durchlaufen
for (let i = 0; i < files.length; i++) {
    let file = files[i];

    // Dateityp prüfen.
    if (!file.type.match('image.*')) {
        continue;
    }

    // Datei in den Request setzen
    formData.append('photos[]', file, file.name);
}

Die Typ-Eigenschaft der Datei (file.type) wird als String zurückgegeben. Darum reicht ein einfaches JavaScript match() auf den gewünschten Dateityp. Wenn der Dateityp nicht passt, wird die Datei übersprungen (continue).

Am Ende der for-Schleife fügt die append-Methode des formDate-Objekts die Datei zur Übertragung hinzu.

FormData.append()

FormDate erspart Zusammenstellen eines Post-Requests / JSON-Objekts aus den Eingabefeldern des Formulars und kann Files, Blobs und Strings übernehmen und versenden.

// Files
formData.append(name, file, filename);

// Blobs
formData.append(name, blob, filename);

// Strings
formData.append(name, value); 

XMLHttpRequest

Der XMLHttpRequest übernimmt die Kommunikation mit der Anwendung auf dem Server. Die Methode hat drei Parameter: die HTTP-Methode (POST), die URL der Anwendung auf dem Server (upload.php) und ein Boolscher Wert, der festlegt, dass der Request asynchron erfolgt.

// XMLHttpRequest aufstellen
var xhr = new XMLHttpRequest();

// Verbindung zur Anwendung aufbauen
xhr.open('POST', 'upload.php', true);

// Platzhalter für den Ladefortschritt

Warten auf eine Antwort vom Server

Der Datei-Upload braucht einen Event Listener, der das onload-Event abfängt. Die status-Eigenschaft des xhr-Objekts zeigt an, ob der Request erfolgreich ausgeführt wurde.

// Event Handler für die Response vom Server
xhr.onload = function () {
    if (xhr.status === 200) {
    // Dateien wurden hochgeladen
	
        document.querySelector("#result").innerHTML = xhr.responseText;
        uploadButton.innerHTML = 'Upload';
        
    } else {
        document.querySelector("#result").innerHTML = "Fehler beim Upload " + xhr.responseText;
    }
};

Jetzt gibt es nur noch eine Anweisung: den Request absenden:

// Daten übertragen
xhr.send(formData);

Thumbnails vor dem Datei-Upload zeigen

Die Anzeige der Vorschaubilder fehlt noch im Script. Die Thumbnails werden angezeigt, wenn der Benutzer Dateien ausgewählt hat – beim change-Event auf das Input-Element (input id="photos"), nicht innerhalb von form.addEventListener.

Das FileReader()-Objekt liest den Rohdaten-Puffer aus dem FileList-Objekt, das durch die Auswahl der Dateien über das Input-Element erzeugt wurde. FileReader.onload feuert, wenn der Lesevorgang beendet ist.

document.getElementById('photos').addEventListener('change', function (event) {
    let files = event.target.files; // FileList object
    for (let i = 0, f; f = files[i]; i++) {
        if (!f.type.match('image.*')) {
            continue;
        }
        let reader = new FileReader();
        reader.onload = (function(theFile) {
            return function(e) {
                let span = document.createElement('span');
                span.innerHTML = ['<img class="thumb" src="', e.target.result, '" title="', escape(theFile.name), '"/>'].join('');
                document.querySelector ('.filelist').insertBefore(span, null);
            };
        })(f);
        reader.readAsDataURL(f);
    }
    document.querySelector ('.filelist').innerHTML = '';
});

FileReader.readAsDataURL() startet das Einlesen und enthält am Ende die Rohdaten der Data-URL.

document.querySelector ('.filelist').insertBefore(span, null) fügt die Thumbnails am Ende der Liste ein.

progress – der Fortschrittsbalken

Javascript konnte zwar den Dateinamen, den Dateityp sowie Höhe und Breite des Bildes lesen, aber hatte keinen Zugriff auf die Größe der Datei auf dem lokalen Rechner des Benutzers.

Die Funktion für den Ladefortschritt:

xhr.upload.onprogress = function (e) {
    let percentUpload = Math.floor(100 * e.loaded / e.total);
    progress.value = percentUpload;
}

Jetzt gibt das xhr-Objekt zurück:

event.loaded
Die Menge der Daten, die aktuell übermittelt wurden
event.total
Die Menge der Daten, die insgesamt zu übertragen sind

Mit percentUpload wird das value-Attribut des progress-Elements gefüttert. Je nachdem, wie groß der Upload ist, springt der Wert schnell von 0 auf 100 oder zeigt bei großen Datenmengen deutlich den Fortschritt des Uploads.

Das Script und auch die PHP-Anwendung, in der die Daten geprüft und behandelt werden, sind nur ein Basis-Gerüst für einen Dateiupload. Dafür ist das Gerüst überschaubar und zeigt die generellen Abläufe des Uploads.

Javascript als RAW anzeigen.

Fotos von mobilen Geräten hochladen

Mobile Geräte wie Tablet oder Smartphone bieten den Upload von Dateien aus dem Album des Geräts an. multiple="true" funktioniert allerdings nicht, wenn Bilder aus der Kamera geladen werden sollen.

Fotos aus der Kamera werden mit

	<input type="file" accept="image/*" capture="camera" />

geladen. Das funktioniert zurzeit allerdings nur mit Webkit-Browsern (Chrome, Safari).