File Upload
Eine der häufigsten Aufgaben im Webdesign ist das Senden von Formulareingaben. Oft will man jedoch nicht nur Daten, sondern ganze Dateien wie Bilder, PDFs oder Text-Dokumente hochladen.
Dieses Tutorial zeigt, wie Sie ein Eingabeformular für den Datei Upload erstellen und welche browserseitigen Sicherheitsvorkehrungen Sie beachten müssen.
Inhaltsverzeichnis
input type="file"
Mithilfe des Formularelements input type="file" kann der Anwender eine Datei von seinem lokalen Rechner zusammen mit dem Formular übertragen (Upload). Durch das Attribut type="file"
erstellt der Browser automatisch ein Eingabefeld für den Pfad zur Datei und einen Button mit dessen Hilfe eine lokale Datei ausgewählt werden kann. Die Größe des Eingabefeldes kann wie üblich mit dem size-Attribut festgelegt werden.
Dieses Element funktioniert nur mit der POST
-Methode und nicht mit GET
. Außerdem muss im Formular das enctype="multipart/form-data"-Attribut notiert werden, da sonst nur der Name und nicht die Datei selbst übertragen wird.
accept
Mithilfe des accept-Attributs kann angegeben werden, welche Dateitypen für den Upload vorgesehen sind. Sie können in diesem Attribut eine durch Komma getrennte Liste von Dateitypangaben hinterlegen, die der Browser dafür nutzen kann, die Dateien, aus denen der Anwender im Dateidialog auswählen kann, vorzufiltern. Der Anwender kann diese Filterung jedoch übergehen. Als Dateityp können Sie auf Computern, die Extensions benutzen, eine File Extension (.txt
, .png
, .exe
) verwenden, eine MIME-Typangabe (text/plain
) oder eine MIME-Typ Sammelangabe ("audio/*). Die im Beispiel verwendete Angabe text/*
ist hingegen zwiespältig zu sehen: Die HTML Spezifikation nennt ausdrücklich nur audio, video und image als gültige Typen für Sammelangaben. Es kann also sein, dass der Browser diese accept-Angabe ignoriert und dem Anwender einfach alle Dateien anbietet.
<form method="post" enctype="multipart/form-data">
<label>Wählen Sie eine Textdatei (*.txt, *.html usw.) von Ihrem Rechner aus.
<input name="datei" type="file" size="50" accept="text/*">
</label>
<button>… und ab geht die Post!</button>
</form>
Hier ist die Breite des Eingabefeldes auf 50 Zeichen festgelegt und es sind alle Textdateien erlaubt. Zusätzlich sei noch erwähnt, dass es sinnvoll ist einen Submit-Button hinzuzufügen, da diverse Browser neueren Datums ein unmittelbares Ausführen über das Textfeld mit dem Dateipfad der gewählten Datei der Aktion nicht mehr zulassen.
Information: Kann ich Datei-Upload-Formulare vorbelegen?
Aus Sicherheitsgründen ist das Vorbelegen dieses Formularelements mithilfe des value
-Attributs nicht möglich.
mehrere Dateien auf einmal hochladen
Mit dem booleschen Attribut multiple kann eine Mehrfachauswahl zugelassen werden, sodass anschließend mehrere Dateien vom Browser an den Server übermittelt werden. Damit die verwendete serverseitige Script-Sprache mit mehreren Dateien zu diesem input
-Feld umgehen kann, ist es unter Umständen notwendig einen geeigneten Namen im name
-Attribut zu notieren (PHP benötigt z. B. ein eckiges Klammernpaar wie name="datei[]"
, damit nicht nur die letzte übertragene Datei erkannt wird).
<form action="manage_uploads.php" method="post" enctype="multipart/form-data">
<label>Wählen Sie die hochzuladenden Dateien von Ihrem Rechner aus:
<input name="datei[]" type="file" multiple>
</label>
<button>hochladen</button>
</form>
Der empfangende Webserver kann aus z. B. Sicherheitsgründen ein Limit für die Anzahl und die Datenmenge der zu empfangenden Dateien haben. Als HTML-Autor haben Sie darauf keinen Einfluss und selbst als Programmierer der serverseitigen Programmlogik haben Sie unter Umständen keine Möglichkeit diese Einstellungen des Servers zu ändern. In solchen Fällen benötigt man eine Lösung, bei der vom Browser die Datenmenge in passend kleinen Teilen übertragen wird, um sie dann auf der Serverseite wieder zusammen zu setzen.
File API
Mit JavaScript und der HTML5 File API können Sie nun ihr HTML-Formular durch weitere Funktionen erweitern, die dem Benutzer der Seite mehr Komfort bieten.[1][2]
Interessant ist hierbei vor allem, dass die APIs die Möglichkeit bieten den Uploadfortschritt zu verfolgen, Thumbnails (Miniaturansichten) der Dateien anzuzeigen, sowie der Einsatz von Drag and Drop. Letzteres ist sehr bequem um Dateien vom Desktop oder aus Ordnern direkt in die Webanwendung zu ziehen, in die die Datei geladen werden soll.
Multi Upload von Dateien
Das Eingabefeld besteht eigentlich aus mehreren im Shadow DOM versteckten Elementen:
Bei der Auswahl einer Datei wird deren Name im versteckten span-Element angezeigt. Bei mehreren ausgewählten Dateien erscheint aber nur die Anzahl. Um dem Benutzer die Namen, MIME-Typen und auch die Dateigröße anzuzeigen, erweitern wir unser Code-Snippet aus dem letzten Abschnitt.
document.getElementById('dateien').addEventListener('change', dateiauswahl, false);
function dateiauswahl(evt) {
// FileList-Objekt des input-Elements auslesen,
// wenn das change-Event ausgelöst wurde (event.target)
var files = evt.target.files;
// Deklarierung eines Array Objekts mit Namen "fragmente". Hier werden die Bausteine
// für die erzeugte Listenausgabe gesammelt.
var fragmente = [];
// Zählschleife; bei jedem Durchgang den Namen, Typ und
// die Dateigröße der ausgewählten Dateien zum Array hinzufügen
for (var i = 0, f; f = files[i]; i++) {
fragmente.push('<strong>', f.name, '</strong> (', f.type || 'n/a', ') - ', f.size, ' bytes','<br>');
}
// Alle Fragmente im fragmente Array aneinanderhängen und
// alles als HTML-Inhalt in das output-Element mit id='dateiListe' einsetzen.
document.getElementById('dateiListe').innerHTML = fragmente.join('');
}
Sobald im Formular etwas geändert wird, feuert das change-Event und ruft die Funktion dateiauswahl()
auf.
Sie liest das FileList-Objekt des input-Elements aus. Anschließend wird ein Array fragmente
angelegt. In diesem Array werden mittels einer Zählschleife für jede ausgewählte Datei deren Dateiname, -typ, sowie Größe gespeichert (über die Arraymethode push()).
Auf eine Formatierung wird bis auf die beiden als Strings eingefügten strong-Tag und das folgende br verzichtet.
document.getElementById('list').innerHTML = fragmente.join('');
setzt all diese Fragmente mittels der Arraymethode join() zu einer langen Zeichenkette zusammen.
Die Registrierung des change-Events, die zum Aufruf der dateiauswahl()
-Funktion erforderlich ist, erfolgt in einem DOMContentLoaded-Handler, damit das Script im <head> der Seite stehen kann.
Auswahl mit Bildvorschau
Noch übersichtlicher ist das Anzeigen kleiner Vorschaubilder - sogenannter Thumbnails.
for (var i = 0, f; f = files[i]; i++) {
...
// nur Bild-Dateien
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
// erzeuge Thumbnails.
var vorschau = document.createElement('img');
vorschau.className = 'thumb';
vorschau.src = e.target.result;
vorschau.title = theFile.name;
document.getElementById('thumblist').insertBefore(vorschau, null);
};
})(f);
// Bilder als Data URL auslesen.
reader.readAsDataURL(f);
}
...
}
In diesem Beispiel werden von den hochzuladenden Bildern Vorschaubilder erstellt (Thumbnailing), die dann im Ausgabefeld output-Element angezeigt werden.
Auswahl mit Drag und Drop
ToDo (weitere ToDos)
function dateiauswahl(evt) {
evt.stopPropagation();
evt.preventDefault();
var gewaehlteDateien = evt.dataTransfer.files; // FileList Objekt
var output = [];
for (var i = 0, f; f = gewaehlteDateien[i]; i++) {
output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
f.size, ' bytes, last modified: ',
f.lastModified.toLocaleDateString(), '</li>');
}
document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
}
function handleDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy';
}
// Initialisiere Drag&Drop EventListener
var dropZone = document.getElementById('dropzone');
dropZone.addEventListener('dragover', handleDragOver, false);
dropZone.addEventListener('drop', dateiauswahl, false);
Hier können Sie Bilder und Dateien aus Ihrem Betriebssystem bequem mit der Maus (Drag & Drop) ziehen und im div mit der id dropzone
ablegen.
Anschließend wird die ausgewählte Datei wie im Beispiel 1 mit einigen Eigenschaften aufgelistet.
Direkter Upload einer einzelnen Datei an einen Server
ToDo (weitere ToDos)
function dateiupload(evt) {
var dateien = evt.target.files; // FileList objekt
// erste Datei auswählen (wichtig, weil IMMER ein FileList Objekt generiert wird)
var uploadDatei = dateien[0];
// Ein Objekt um Dateien einzulesen
var reader = new FileReader();
var senddata = new Object();
// Auslesen der Datei-Metadaten
senddata.name = uploadDatei.name;
senddata.date = uploadDatei.lastModified;
senddata.size = uploadDatei.size;
senddata.type = uploadDatei.type;
// Wenn der Dateiinhalt ausgelesen wurde...
reader.onload = function(theFileData) {
senddata.fileData = theFileData.target.result; // Ergebnis vom FileReader auslesen
/*
Code für AJAX-Request hier einfügen
*/
}
// Die Datei einlesen und in eine Data-URL konvertieren
reader.readAsDataURL(uploadDatei);
}
Dank der Möglichkeit, eine Datei als Data-URL einzulesen, ist es möglich, selbst mit einem gewöhnlichen Webformular eine Datei an einen Server zu senden.
Auswahl mit Textvorschau
function dateiauswahl(evt) {
var dateien = evt.target.files; // FileList object
// Auslesen der gespeicherten Dateien durch Schleife
for (var i = 0, f; f = dateien[i]; i++) {
// nur TXT-Dateien
if (!f.type.match('text/plain')) {
continue;
}
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
// erzeuge "Thumbnails"
var vorschau = document.createElement('p');
vorschau.className = 'thumb';
vorschau.src = e.target.result;
vorschau.title = theFile.name;
document.getElementById('list').insertBefore(vorschau, null);
};
})(f);
// Klartext mit Zeichenkodierung UTF-8 auslesen.
reader.readAsText(f, utf8);
}
}
// Auf neue Auswahl reagieren und gegeenenfalls Funktion dateiauswahl neu ausführen.
document.getElementById('files').addEventListener('change', dateiauswahl, false);
In diesem Beispiel wird von den hochzuladenden Texten der komplette Inhalt übernommen, welcher dann im Ausgabefeld output-Element angezeigt wird.
(billiger) Hex-Viewer im Browser
function dateiauswahl(evt) {
var dateien = evt.target.files; // FileList object
// Auslesen der gespeicherten Dateien durch Schleife
for (var i = 0, f; f = dateien[i]; i++) {
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
// Bytedaten übernehmen
var byteBlock = e.target.result; // ArrayBuffer Objekt
// ein Objekt um den Byteblock auszulesen
var dataViewer = new DataView(byteBlock);
var hexView = "";
// Hex-Werte übernehmen
for (var j = 0; j < f.size; j++) {
hexView += parseInt(dataViewer.getUint8(j), 16) + " ";
}
// Das Leerzeichen am Ende entfernen
hexView = hexView.substr(0,hexView.length-1);
// erzeuge "Thumbnails"
var vorschau = document.createElement('p');
vorschau.className = 'thumb';
vorschau.src = hexView
vorschau.title = theFile.name;
document.getElementById('list').insertBefore(vorschau, null);
};
})(f);
// Datei als ByteArray auslesen auslesen.
reader.readAsArrayBuffer(f);
}
}
// Auf neue Auswahl reagieren und gegebenenfalls Funktion dateiauswahl neu ausführen.
document.getElementById('files').addEventListener('change', dateiauswahl, false);
Hiermit können die Hexwerte jeder beliebigen Datei ausgelesen werden.
Siehe auch
- PHP/File Upload
sehr ausführlicher Artikel mit dem Schwerpunkt Sicherheit
Weblinks
- html5rocks: Reading files in JavaScript using the File APIs
- hexed.it: HexEditor
- MDN: FileReader
- MDN: ArrayBuffer
- MDN: DataView