Fortsetzung
Wer sich durch
Teil 1 und
Teil 2 dieses Tutorials die Grundlagen angeeignet hat, erfährt nun im 3. Teil, wie man über ein Formular Dateien auf einen Server laden kann. Obwohl der eigentliche Hochlade-Prozess nur ein Ein-Zeiler ist, muß man doch einiges an Know-How mitbringen, damit es hier keine bösen Überraschungen gibt.
In diesem Teil lernen wir: Wie man überprüft ob überhaupt ein Upload stattfand; Ob es bei der Übertragung zu Fehler kam; Die Datei auf gültige Formate zu prüfen; Wie die Datei an ihren Bestimmungsort kommt; Dateirechte setzen, daß man die Datei auch wieder löschen kann.
Das Upload Formular
Schon der HTML-Teil eines Formulars für einen Datei Upload unterscheidet sich von einem "normalen" Formular das ausschließlich zum übertragen von Text-Informationen benutzt wird. Schauen wir uns zunächst einmal das HTML eines solchen Formulars an.
HTML-Code:
<html>
<head>
<title>Datei Upload</title>
</head>
<body>
<form name="DateiUpload" id="DateiUpload" method="post" action="" enctype="multipart/form-data">
<input type="file" name="datei" id="datei" /><br>
<input type="submit" name="submitbutton" id="submitbutton" value="Datei hochladen">
</form>
</body>
</html>
Der erste große Unterschied findet schon direkt im <form>-Tag statt. Sobald ein Formular eine Datei hochladen können soll, muß der
enctype angegeben werden.
Code:
enctype="multipart/form-data"
Ohne diesen Zusatz kann man keine Dateien auf einen Server übertragen!
Als Input-Type müssen wir "file" benutzen. Dies erzeugt ein Upload-Feld mit dem man automatisch über den "Durchsuchen"-Button die Festplatte nach der entsprechenden Datei durchsuchen kann. Das ist schon alles was seitens HTML nötig ist. Binden wir noch kurz die uns bekannte Ausgabe-Funktion im PHP-Teil ein und machen einen Testlauf. Das Script sieht nun wie folgt aus ...
PHP-Code:
<?php
if (isset( $_POST['submitbutton'] ))
{
echo "<pre>" .print_r( $_POST, true ). "</pre>";
}
?>
<html>
<head>
<title>Datei Upload</title>
</head>
<body>
<form name="DateiUpload" id="DateiUpload" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" enctype="multipart/form-data">
<input type="file" name="datei" id="datei"><br>
<input type="submit" name="submitbutton" id="submitbutton" value="Datei hochladen">
</form>
</body>
</html>
Ein erster Aufruf im Browser, als Datei wähle ich testweise ein JPG Bild mit dem Name "sonnenblume.jpg" und klicke auf den Submitbutton. Hmm, irgend etwas scheint hier nicht zu stimmen, denn die Ausgabe sieht so aus:
Code:
Array
(
[submitbutton] => Datei hochladen
)
Sollte hier nicht auch
[datei] => "sonnenblume.jpg" stehen, so das wir über
$_POST['datei'] auf unser Bild zugreifen können? Die Antwort ist: Nein!
Dateien die über ein Formular hochgeladen werden verfügen über ein eigenes, Superglobales Array, das auf den Namen
$_FILES hört. Texteingaben und Datei Uploads werden also, obwohl über ein und daselbe Formular kommend, getrennt behandelt. Das ist auch gut so, denn das $_FILES Array hält viel mehr Informationen über eine hochgeladene Datei bereit mit der wir arbeiten können.
Ändern wir doch mal das ...
PHP-Code:
echo "<pre>" .print_r( $_POST, true ). "</pre>";
... ab in ein ...
PHP-Code:
echo "<pre>" .print_r( $_FILES, true ). "</pre>";
... und sehen was uns PHP mitzuteilen hat. Die Ausgabe sieht nun wie folgt aus:
Code:
Array
(
[datei] => Array
(
[name] => sonnenblume.jpg
[type] => image/jpeg
[tmp_name] => C:\xampp\tmp\php6.tmp
[error] => 0
[size] => 112899
)
)
Aha, das sieht doch schon besser aus. Hier sehen wir eine ganze Menge Info über unser hochgeladenes Bild. Das
Superglobale Array $_FILES hält für uns verschieden Informationen bereit. Im einzelnen sind das:
- $_FILES['name'] - Der Dateiname der hochgeladenen Datei
- $_FILES['type'] - Der MIME Typ der Datei
- $_FILES['tmp_name'] - Der temporäre Name der Datei auf dem Server
- $_FILES['error'] - Der Fehlercode der Aufschluß über den Upload gibt, im einzelnen sind das:
- 0 - Es liegt kein Fehler beim Upload vor
- 1 - Die hochgeladene Datei ist größer als der maximal erlaubte Wert in der php.ini
- 2 - Die hochgeladene Datei ist größer als der maximal erlaubte Wert der durch MAX_FILE_SIZE im Formular definiert wurde
- 3 - Die Datei wurde nur teilweise hochgeladen
- 4 - Es wurde keine Datei hochgeladen
- $_FILES['size'] - Die Dateigröße in Byte
Noch einmal zurück zum HTML-Teil. Wie im Error-Code 2 zu sehen ist, kann man mit MAX_FILE_SIZE eine maximal erlaubte Dateigröße vorgeben. Dies ist aber eher unzuverlässig und kann umgangen werden, deswegen habe ich es oben nicht in's HTML eingebaut. Der Vollständigkeit wegen möchte ich hier aber noch kurz zeigen wie man einen Wert festlegt.
HTML-Code:
<input type="hidden" name="MAX_FILE_SIZE" value="50000">
Der Wert wird durch ein verstecktes Feld (type="hidden") übergeben und das name-Attribut muß den Namen
MAX_FILE_SIZE tragen. Zwischen Groß-/Kleinschrift wird hier unterschieden! Durch das value-Attribut wird die erlaubte Größe in Byte festgelegt, hier wären das ca. 50 KB. Wichtig ist hierbei noch, daß die MAX_FILE_SIZE
vor dem eigentlichen Dateifeld festgelegt werden muß.
Code:
Richtig!
<input type="hidden" name="MAX_FILE_SIZE" value="50000">
<input type="file" name="datei" id="datei">
Falsch!
<input type="file" name="datei" id="datei">
<input type="hidden" name="MAX_FILE_SIZE" value="50000">
In Ordnung, damit sollten die Grundlagen für den Datei Upload geklärt sein. Schauen wir uns nun an wie wir mit PHP die hochgeladene Datei verarbeiten können.
Hochgeladene Datei mit PHP verarbeiten
Bevor wir uns nun direkt in's Getümmel stürzen müssen einige Vorüberlegungen gemacht werden. Die beiden wichtigsten Punkte die uns im Moment interessieren sind:
1.) Läuft das Script später auf einem Windows oder Linux System; Welche Browser rufen die Datei später ab? Wissen wir nicht?! Also sollten wir dafür sorgen das Dateinamen weder Umlaute noch Leerzeichen enthalten, damit es da keine Probleme gibt.
2.) Welche Datei-Typen möchten wir für einen Upload zulassen? Sicherlich keine .bat oder .exe Dateien, deswegen sollten wir eine Whitelist mit Dateiendungen anlegen.
PHP-Code:
$erlaubte_Dateiendungen = array( "jpg", "gif", "zip" );
$Dateiname_bereinigen = array( 'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'ß' => 'ss', ' ' => '_' );
Damit haben wir festgelegt welche Zeichen im Dateiname ersetzt werden sollen und welche Dateiendung die Dateien haben dürfen. In unserem Fall erlauben wir nur JPG, GIF und Zip Dateien. Jetzt wird's ernst, denn es geht an's Code schreiben. Zuerst mal das komplette Script und im Anschluß daran gehen wir den Code durch um zu sehen was da passiert.
PHP-Code:
<?php
// Wurde das Formular abgeschickt?
if (isset( $_POST['submitbutton'] ))
{
// Whiteliste Dateiendungen und Ersetzungen
$Erlaubte_Dateiendungen = array( "jpg", "gif", "zip" );
$Dateiname_bereinigen = array( 'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'ß' => 'ss', ' ' => '_' );
// Pruefen ob die hochgeladenen Datei mehr als 0 Byte hat
// Hat sie das nicht, wurde auch nichts hochgeladen, logisch, was?! ;)
if ($_FILES['datei']['size'] > 0)
{
// Dateiendung der hochgeladenen Datei abtrennen
$UploadDateiEndung = array_pop( explode( ".", strtolower( $_FILES['datei']['name'] ) ) );
// Schauen ob die Endung der hochgeladenen Datei in der Whitelist steht
if (!in_array( $UploadDateiEndung, $Erlaubte_Dateiendungen ))
{
die( "Die angehängte Datei hat eine nicht erlaubte Dateiendung!" );
}
// Neuer Dateiname erzeugen indem Umlaute und Leerzeichen umgewandelt werden
$DateiNameNeu = strtr( strtolower( $_FILES['datei']['name'] ), $Dateiname_bereinigen );
// UMASK resetten um Dateirechte zu ändern (wird nur fuer Linux benoetigt, Windows ignoriert das)
$umask_alt = umask( 0 );
// Hochgeladenen Datei verschieben
if (@move_uploaded_file( $_FILES['datei']['tmp_name'], $DateiNameNeu ))
{
// Die Datei wurde erfolgreich an ihren Bestimmungsort verschoben
/* ***************************************************************************************** */
/* *** Hier koennte Code stehen um Email zu versenden oder Datenbank-Eintraege zu machen *** */
/* ***************************************************************************************** */
// Dateirechte setzen, damit man später die Datei wieder vom FTP bekommt und die UMASK auf den alten Wert setzen
@chmod( $DateiNameNeu, 0755 );
umask( $umask_alt );
}
else
{
// UMASK resetten
umask( $umask_alt );
// Hier steht Code der ausgefuehrt wird, wenn der Upload fehl schlug
}
}
}
?>
<html>
<head>
<title>Datei Upload</title>
</head>
<body>
<form name="DateiUpload" id="DateiUpload" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" enctype="multipart/form-data">
<input type="file" name="datei" id="datei"><br>
<input type="submit" name="submitbutton" id="submitbutton" value="Datei hochladen">
</form>
</body>
</html>
Wie man auf den ersten Blick sieht, muß man hier etwas mehr machen und beachten um mal eben eine kleine Datei auf den Server zu schieben. Aber keine Angst, das mag etwas verwirrend aussehen, ist aber sehr einfach zu verstehen. Der Code sollte ausreichend kommentiert sein um zu verstehen was da vor sich geht. Dennoch möchte ich zu der ein oder anderen Zeile noch etwas sagen.
PHP-Code:
$UploadDateiEndung = array_pop( explode( ".", strtolower( $_FILES['datei']['name'] ) ) );
Hier wird die Dateiendung der hochgeladenen Datei abgetrennt und in $UploadDateiEndung gelegt, damit wir diese überprüfen können. Dazu wird der Dateiname mit
explode() an auftretenden Punkten getrennt. Mit
array_pop() greifen wir uns nur das letzte Element des durch
explode() erzeugten Array. Wieso das letzte Element, normalerweise hat doch ein Array nur 2 Elemente, wenn wir am Punkt trennen; Dateiname und Dateiendung!? Nun, es könnte ja sein das ein Dateiname z.B. so aussieht: ich.bin.ein.huebsches.bild.jpg
In dem Fall hätten wir nach
explode() ein Array mit 6 Elementen. Würden wir hier das 2. Element nehmen, hätten wir nicht "jpg" sondern "bin" und es würde zu einem Fehler kommen.
Mit der Abfrage ...
PHP-Code:
if (!in_array( $UploadDateiEndung, $Erlaubte_Dateiendungen ))
... schauen wir nach ob die Dateiendung der hochgeladenen Datei im Array der erlaubten Dateiendungen ist. Falls nicht, wird die Anweisung abgebrochen und eine Fehlermeldung ausgegeben.
Die Zeile in der alles passiert ist ...
PHP-Code:
if (@move_uploaded_file( $_FILES['datei']['tmp_name'], $DateiNameNeu ))
Mit
move_uploaded_file() wird eine Datei aus dem Temporären Ordner an eine neue Position verschoben. Für hochgeladenen Dateien sollte diese Funktion benutzt werden und nicht etwa
copy().
Es sollte erwähnt sein, daß die Datei, sofern kein Pfad angegeben wurde, in das selbe Verzeichnis verschoben wird in dem sich das Script befindet. Um die Datei an einen anderen Bestimmungsort zu verschieben ist es erforderlich das ein Pfad angegeben wird, z.B.
PHP-Code:
if (@move_uploaded_file( $_FILES['datei']['tmp_name'], "/bilder/pflanzen/" .$DateiNameNeu ))
Das hochgeladene Bild würde nun nach http://www.domain.tld/bilder/pflanzen/sonnenblume.jpg verschoben werden.
Wenn das Bild in einen anderen Ordner verschoben wird als in dem das Script liegt,
muß auch der Pfad beim nachfolgenden
chmod() angepasst werden, da es sonst zu Fehlverhalten kommen kann! Zum Beispiel:
PHP-Code:
@chmod( "/bilder/pflanzen/" .$DateiNameNeu, 0755 );
(Vielen Dank an Guin für die Anregung das "Pfad-Problem" mit in's Beispiel aufzunehmen.)
Achtung Falle!
Beim verschieben muß darauf geachtet werden das man die Datei mit dem temporären Namen des Uploads (
$_FILES['datei']['tmp_name']) verschiebt. Würde man versuchen die Datei mit dem Originalname zu verschieben, der sich in
$_FILES['datei']['name'] befindet, käme es zu einem Fehler, da PHP bis zu diesem Zeitpunkt nur den temporären Name kennt!
Erst während des verschiebens mit
move_uploaded_file() wird die Datei in $DateiNameNeu umbenannt.
Wichtig!
Die hochgeladene Datei muß sofort verarbeitet werden, da temporäre Dateien beim beenden des Script gelöscht werden und nicht mehr zur Verfügung stehen! Es ist also
nicht möglich z.B. den Tmp Name und Original Name in einer Session abzulegen und auf einer Folgeseite zu verarbeiten! Das ist wichtig zu wissen, falls man z.B. mehrseitige Formulare mit Upload hat.
Schlußwort
Damit sollte soweit alles zum Thema Datei Upload gesagt sein. Gleichzeitig stellt dieses Kapitel auch den Schluß des 3-teiligen Formular Tutorials dar. Jetzt gibt's noch die verwendeten Funktionen als Link zur Online Dokumentation. Viel Spaß beim testen und experimentieren mit Formulare.
Links