Javascript Datei-Upload mit Vorschau und fetch

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

Datei-Upload: Das FormDate-API sammelt und übermittelt Daten effizienter als der direkte Zugriff auf Formularelemente. FormData kann Dateien einfach mit der Methode append () hinzufügen und die Felder müssen nicht wie bei einem Formular im Voraus bekannt sein, sondern können dynamisch generiert werden.

Mehrere Dateien auf den Server laden

FormData kümmert sich automatisch um die Codierung der Daten mitsamt der Umsetzung von Leerzeichen und Sonderzeichen. Das Fetch-API versendet die Daten asynchron ohne viele Aufhebens im Hintergrund.

<input type="file" id="photos" name="photos[]" multiple>
<progress id="progress" value="100" max="100" style="display: none;"></progress>

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

FileReader für Thumbnail-Vorschau

Ein einfaches 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, der Benutzer würde keine Vorschaubilder sehen und weder über Fehler noch über Erfolg der Aktion unterrichtet. Das FileReader-Objekt liest über input type="file" Dateien vom Rechner des Benutzers in eine FileList. FileReader.readAsDataURL () enthält bei Erfolg eine data: URL als Darstellung der Datei.

const fileSelect = document.getElementById('photos');

document.getElementById('photos').addEventListener('change', function (event) {
	let files = event.target.files; // FileList
	for (let i = 0, f; f = files[i]; i++) {
		if (!f.type.match('image.*')) continue;

		const reader = new FileReader();
		reader.onload = (function(theFile) {
			return function(e) {
				const img = document.createElement ("img");
				img.src = e.target.result;
				img.classList.add("thumb");
				document.querySelector ('.filelist').append(img)
			};
		})(f);
		reader.readAsDataURL(f);     
	}
	const button = document.createElement ("button");
	button.id = "upload";
	button.innerHTML = "Hochladen";
	document.querySelector ("body").append (button);
	button.addEventListener ("click", upload); 
});

Wenn das input-Element mehr als eine Datei erlaubt, ist input.files ein Array-ähnliches Objekt mit den Dateien. Kann nur eine einzelne Datei geladen werden, liegt sie in input.files[0]. 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 auch der Browser u.U. sichtbar Zeit, um die Thumbnails zu erstellen.

Am Ende erzeugt das Skript den Button für den Upload.

FormData.append()

FormData erspart das Zusammenstellen eines Post-Requests / JSON-Objekts aus den Eingabefeldern des Formulars, kann Files, Blobs und Strings mit append übernehmen sowie benutzerdefinierte Schlüssel-Wert-Paare (z.B. den Benutzernamen) hinzufügen und versenden.

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

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

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

Der tatsächliche Upload auf den Server beginnt, wenn der User die Daten mit dem »Laden«-Button abschickt. Das progress-Element wird sichtbar.

function upload () {
	document.querySelector(".result").innerHTML = "";
	document.querySelector('progress').style = "display: block";
	
	const files = fileSelect.files;
	const formData = new FormData();
	
	for (const item of files) {
		if (!item.type.match('image.*')) {
			continue;
		}
		formData.append('photos[]', item, item.name);
	};
	
	formData.append('username', 'Martina Schmitz');
	
	fetch ("upload.php", {
		method: "POST",
		body: formData
	})
	.then (response => response.json ())
	.then (data => {
		console.log ("Antwort vom Server", data);
		document.querySelector ("#upload").remove ();
		document.querySelector ('.filelist').innerHTML = '';
		document.querySelector (".result").innerHTML = data.data + " Status: "+ data.status;
	});
};

PHP Upload mehrerer Dateien

Nur rudimentär: Nur PNG ist zugelassen, Dateigröße beschränkt. PHP lädt Dateien zunächst in ein temporäres Verzeichnis, erst wenn das so funktioniert, werden die Dateien in das endgültige Verzeichnis auf dem Server bewegt und aus dem temporären Verzeichnis gelöscht.

<?php
//  JSON – Ausgabe
function outputJSON($msg, $status = 'error'){
    header('Content-Type: application/json');
    die(json_encode(array(
        'data' => $msg,
        'status' => $status
    )));
}

/** Über alle Dateien laufen 
    und den Pfad für den Dateispeicher setzen
**/
$target_dir = " … Pfad zum Verzeichnis auf dem Server … ";

if ( isset($_FILES['photos']['name']) ) {
	$total_files = count($_FILES['photos']['name']);

	for($key = 0; $key < $total_files; $key++) {

		// Check if file is selected
		if (isset($_FILES['photos']['name'][$key]) 
                      && $_FILES['photos']['size'][$key] > 0) {
			
			// Check filetype
			if($_FILES['photos']['type'][$key] != 'image/png'){
				outputJSON('Dateityp muss PNG sein.');
			}
			
			if(!getimagesize($_FILES['photos']['tmp_name'][$key])){
				outputJSON('Datei ist kein Bild.');
			}
			
			// Check filesize
			if($_FILES['photos']['size'][$key] > 500000){
				outputJSON('Dateigröße überschreitet Max 500KB.');
			}
			
			// Check ob der Dateiname bereits existiert
			if(file_exists ($target_dir . $_FILES['photos']['name'][$key])){
				outputJSON('Eine Datei mit diesem Namen existiert bereits.');
			}

			$original_filename = $_FILES['photos']['name'][$key];
			$target = $target_dir . basename($original_filename);
			$tmp  = $_FILES['photos']['tmp_name'][$key];
			move_uploaded_file($tmp, $target);
		}
	}
   outputJSON ( $total_files . ' Dateien in das Verzeichnis UPLOADS geladen ',  'OK');  
}

Fotos von mobilen Geräten hochladen

Auch die mobilen Geräte wie Tablet oder Smartphone bieten den Upload von Dateien aus dem Album des Geräts an.

Firefox loading images from mobile device
Fotos vom iPad mit Firefox laden

Fotos aus der Kamera werden geladen mit

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