File Upload mit Javascript und PHP

File Upload mit Javascript und PHP

Der Upload mehrerer Dateien auf den Server ist dank der Abkürzung fetch heute mit wenigen Zeilen deutlich intuitiver als die frühen Anläufe mit dem XMLHttpRequest in seiner Urform oder selbst AJAX 2.

fetch erspart das komplizierte Prozedere des asynchronen Ablaufs und hält die Anweisungen linear und lesbar.

18-12-15 SITEMAP

Das HTML-Formular für den File-Upload

Das Formular benutzt die Methode HTTP POST, um die Daten zum Server zu senden. enctype="multipart/form-data" ist ein erforderliches Attribut, wenn Dateien auf den Server geladen werden sollen.

<form id="uploadform" method="post" enctype="multipart/form-data">	
	<input id="files" type="file" name="files[]" multiple>
	<input type="submit" value="Upload File" name="submit">
</form>

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

<script src="simple-upload.js"></script>

Die Daten werden als Array von Dateien (files[]) hochgeladen. Der Name für das Array – files[] – ist beliebig und könnte auch uploads[] oder bilder[] lauten.

File-Upload mit Javascript

Das form-Tag hat kein action-Attribut und diese Seite ist keine PHP-Seite, sondern eine einfache HTML-Seite. Der Versuch, Dateien auf den Server zu laden, würde mit diesem unvollständigen Formular im Nichts enden. Erst Javascript sendet die Dateien an das PHP-Script auf dem Server.

Der Grund dafür: Ohne Javascript würden wir von der Antwort der PHP-Anwendung auf dem Server nichts mitbekommen. Wir versenden die Formulardaten mit Javascript, weil wir mit Javascript eine Antwort vom Server bekommen.

Javascript
const url = "simple-upload.php";
const form = document.querySelector("#uploadform");

// Ein EventListener wartet auf das submit
form.addEventListener ("submit", function (evt) {
	evt.preventDefault ();
	const files = document.querySelector('[type=file]').files;
    const formData = new FormData();
    
	for (let i = 0; i < files.length; i++) {
		let file = files[i];
		formData.append('files[]', file)
	}
	
	fetch (url, {
		method: "POST",
		body: formData,
	}).then ((response) => {
		console.log (response);
		if (response.status === 200) {
			document.querySelector("#result").innerHTML = "Dateien wurden geladen";
		}
	});
});

const url = "simple-upload.php"; ist der Pfad zum PHP-Script auf dem Server.

evt.preventDefault (); ist die erste Anweisung in der Callback-Funktion des EventListeners und macht genau das: Verhindern, dass die Formulardaten direkt versendet werden.

Alle Dateien werden ohne großes TamTam in der for-Anweisung an ein formData-Objekt gehangen. fetch führt einen XHR-Request zum Server aus und nutzt die Syntax von Javascript Promise mit dem Schlüsselwort then, um Aufgaben sequenziell und ohne verschachtelte Callbacks im Hintergrund auszuführen.

Erster Check: Datei-Upload in PHP angekommen?

In der Browser-Konsole können wir testen, ob die Daten sauber ankommen. Dafür braucht es nur eine PHP-Datei simple-upload.php auf dem Server, die einfach den Inhalt des superglobalen Arrays $_FILES ausgibt.

<?php print_r($_FILES);

Mehr wird für einen Schnelltest nicht benötigt.

In den Entwickler-Werkzeugen bzw. der Browser-Console sollte die Antwort des PHP-Scripts als Response ungefähr wie folgt angezeigt werden.

Response
ok: true
redirected: false
status: 200
statusText: ""
type: "basic"
url: "… upload.php"

Wichtiger Eckpfeiler: status: 200 besagt, dass das Script funktioniert.

Wie die Anfrage im PHP-Script angekommen ist, sehen wir ebenfalls in der Browser-Console unter Netzwerk / Netzwerkanalyse / XHR / Antwort, z.B.

Browser-Konsole Netzwerk XHR Antwort vom Server prüfen
XHR Response vom Server
Array
(
    [files] => Array
        (
            [name] => Array
                (
                    [0] => image1.jpg
                    [1] => image2.jpg
                )
            [type] => Array
                (
                    [0] => image/jpeg
                    [1] => image/jpeg
                )
            [tmp_name] => Array
                (
                    [0] => /tmp/phpOzHyr7
                    [1] => /tmp/phpRDlkFG
                )
            [error] => Array
                (
                    [0] => 0
                    [1] => 0
                )
            [size] => Array
                (
                    [0] => 17418
                    [1] => 17555
                )
        )
)

Dateien im PHP-Script speichern

Die Abfrage am Anfang prüft, ob die Anfrage per POST versendet wurde, dann ob die gelieferten Daten Dateien sind.

Als Dateityp werden nur Bilder vom Typ JPG, PNG und SVG akzeptiert.

Dateien, die auf den Server geladen werden, landen zuerst in einem TEMP-Verzeichnis des Servers bzw. des Webspace. Dieser Speicher ist so flüchtig, dass er am Ende des Scripts schon wieder gelöscht ist.

In diesem temporären Verzeichnis bedient sich das PHP-Script.

PHP-Script
if ($_SERVER ["REQUEST_METHOD"] === "POST") {
	if (isset($_FILES['files'])) {
		$errors = [];
		$path = "filedirectory/";
		$extensions = ["jpg", "jpeg", "png", "svg"];
		$all_files = count ($_FILES ["files"]["tmp_name"]);
		for ($i = 0; $i < $all_files; $i++) {
			$file_name = $_FILES['files']['name'][$i];
			$file_tmp = $_FILES['files']['tmp_name'][$i];
			$file_type = $_FILES['files']['type'][$i];
			$file_size = $_FILES['files']['size'][$i];
			$file_ext = strtolower(end(explode('.', $_FILES['files']['name'][$i])));

			$file = $path . $file_name;
			
			if (!in_array($file_ext, $extensions)) {
				$errors[] = 'Dateityp nicht erlaubt: ' . $file_name . ' ' . $file_type;
			}

			if ($file_size > 2097152) {
				$errors[] = 'Datei zu groß: ' . $file_name . ' ' . $file_type;
			}
			
			if (empty($errors)) {
				move_uploaded_file($file_tmp, $file);
			}
		}
		
		if ($errors) print_r($errors);
	}
}

Innerhalb der for-Anweisung prüft das PHP-Script, ob die Rahmenbedingungen stimmen: Dateityp und Dateigröße. Passt eine Datei nicht ins Schema, gibts eine Fehlermeldung.

Ansonsten bewegt PHP die Daten in das Verzeichnis filedirectory.

Bilder vor dem Upload anzeigen

Wenn wir anhand von Thumbnails sehen, welche Dateien auf den Server geladen werden, fühlen wir uns sicherer. Die Anzeige der Daten ist dank FileReader mit wenigen Zeilen gelöst ( Thumbnails vor dem Datei-Upload zeigen).

Einfach ans Ende des Scripts noch

document.querySelector("#files").addEventListener ("change", function (evt) {
	let files = event.target.files; 
	for (let i = 0, f; f = files[i]; i++) {
		let reader = new FileReader();
		reader.onload = (function(theFile) {
            return function(e) {
                let span = document.createElement('span');
                span.innerHTML = [''].join('');
                document.querySelector ('.filelist').insertBefore(span, null);
            };
        })(f);
        reader.readAsDataURL(f);
	}
})