info@a-coding-project.de

Datei-Uploads mit PHP

Die Online-Festplatten, die fast schon zum Standardangebot eines Providers gehören oder das Webdav-Konzept sind für Datei-Uploads zwei sehr gute Praxisbeispiele. Nicht zu vergessen, sind die Webmail-Dienste, falls eine Email mit Anlage(n) verschickt werden soll. Dazu müssen die Dokumente erstmal auf den Webserver kopiert werden. Für den Upload auf den Webserver müssen keine weiteren Protokolle freigeschaltet werden und über Funktionen bzw. Attribute auf Browser- und Serverseite kann ein Missbrauch verhindert werden.

HTML-Formular für Dateiupload

Zum Anstoßen des Uploads muss die Datei erst ausgewählt werden. Dies geschieht durch eine Variante des input-Tags. Zusätzlich muss in der form-Zeile ein weiteres Attribut angegeben werden, wie im folgenden Beispiel gezeigt wird.

<form action="upload.php" method="post" 
      enctype="multipart/form-data">
<p>Bitte eine Datei auswählen<br><input type=file name="file"></p>
<input type=submit value=UPLOAD>
</form>

Das Attribut enctype muss zwingend angegeben werden. Der Wert des Attributes "multipart/form-data" ist immer gleich. Wenn das Attribut nicht angegeben wird, wird nur der Dateiname aber nicht die Datei selbst übermittelt. Eine weitere zwingende Angabe ist der Wert "post" für das Attribut method. Mit einer anderen Methode funktioniert die Dateiübermittlung nicht.

Hinweise zu weiteren Attributen

In früheren HTML-Standards wurden noch zwei weitere Attribute definiert, die in der form-Zeile mit angegeben werden können. Allerdings werden diese Attribute von den bekannten Browsern in deren aktuellen Version nicht bzw. nicht zuverlässig interpretiert.

  • Für das Attribut maxlength ist eine Bytegröße vorgesehen, die die ausgewählte Datei nicht überschreiten darf.
  • Mit dem Attribut accept kann ein bestimmter Mime-Type bzw. eine Wildcard angegeben werden, so dass nur Dateien mit diesem Typ übermittelt werden dürfen.
Bei input-Elementen gibt es normalerweise das Attribut "value", mit dem eine Standardbelegung angegeben werden kann. Bei der <input type=file>-Variante wird dieses Attribut aus Sicherheitsgründen nicht unterstützt.

Upload-Parameter in der php.ini - Datei

In der Konfigurationsdatei von PHP stehen verschiedene Parameter zur Verfügung, mit denen die Datei-Uploads geregelt werden können. Die Parameter sind in der Sektion "File Uploads" zu finden. Im folgenden Abschnitt werden alle Parameter sowie deren Bedeutung und mögliche Werte erläutert.

Parameter file_uploads

Dieser Parameter kann die Werte "On" und "Off" annehmen. Er aktiviert (Wert: On) bzw. deaktiviert (Wert: Off) pauschal den Upload von Dateien.

Parameter upload_tmp_dir

Nach dem Upload wird die Datei zuerst in ein temporäres Verzeichnis kopiert, bevor sie an ihr entgültiges Ziel kommt. Der Pfad für dieses Zwischenverzeichnis kann an dieser Stelle angegeben werden. Wenn keine Pfadangabe gemacht wird, greift PHP auf den Standardpfad des Betriebssystems für temporäre Dateien zu.

Parameter upload_max_filesize

Dies ist das serverseitige Gegenstück zum HTML-Attribut maxlength. Standardmäßig hat der Parameter den Wert "2M" (= 2 MByte). Man sollte die angegebene Größe nicht zu hoch ansetzen, da das Skript abgebrochen wird, wenn der Dateiupload die maximal mögliche Ausführungszeit des Skriptes überschreitet.

Die folgenden beiden Parameter sind in anderen Sektionen der php.ini zu finden, aber sie stehen trotzdem in unmittelbaren Zusammenhang mit dem Dateiupload.

Parameter post_max_size

Dieser Parameter gehört zur Sektion "Data Handling" und gibt an, welche Datenmenge maximal verarbeitet werden kann, wenn diese mit POST an das PHP-Skript übermittelt wurde. Die Standardeinstellung für diesen Parameter ist "8M" und wenn große Datenmengen übertragen werden, sollte der hier angegebene Wert den Wert von upload_max_filesize überschreiten.

Parameter max_input_time

Der Standardwert dieses Parameters ist 60 Sekunden. Er sagt aus, wie lange ein Skript maximal Zeit hat, um ankommende Daten, die z. B. mit POST übermittelt wurden, zu verarbeiten. Dieser Parameter ist in der Sektion "Ressource Limits" in der Konfigurationsdatei zu finden.

Meine persönliche Empfehlung ist, die Standardwerte aller Parameter nur dann zu ändern, wenn es wirklich notwendig ist. Prinzipiell kann man davon ausgehen, dass die Default-Werte ihren Sinn haben bzw. großzügig genug ausgelegt sind.

PHP-Skript für File-Upload

Zunächst müssen folgende Voraussetzungen geschaffen werden:

  • Der Webserver muss schreibenden Zugriff auf das Zielverzeichnis haben.
  • Wenn im Zielverzeichnis eine Datei mit gleichem Namen existiert, wird diese überschrieben.
Prinzipiell ist ein Dateiupload serverseitig mit einer Skriptzeile erledigt. Mit der Funktion copy(), mit der auch Dateien auf dem Server hin- und herkopiert werden können, wird gleichzeitig auch der Upload von Dateien über HTTP realisiert. Es muss eine Quelle und ein Ziel angegeben werden - fertig! Das bedeutet, dass mit der folgenden Zeile ein Dateiupload komplett bewerkstelligt werden kann:

copy($tmp_filename, "D:/Webdav/test1/" . $filename);

In der Variable $tmp_filename ist der temporäre Dateiname gespeichert, den die ausgewählte Datei beim Upload bekommt, wenn sie im temporären Verzeichnis hinterlegt wird. Die Variable $filename hat als Wert den eigentlichen Namen der Datei. Zum Zugriff auf die beiden Informationen stellt PHP das Array $_FILES zur Verfügung. Der temporäre und eigentliche Dateiname sind zwei Elemente des Arrays. Den vorläufigen Dateinamen bekommt man über das Element $_FILES['file']['tmp_name'] und den eigentlichen Namen über $_FILES['file']['name]. Das "file" entspricht dem Namen des Auswahlfeldes, welches im HTML-Formular definiert wurde.
Die folgende Tabelle zeigt sämtliche Informationen, die über das $_FILES-Array ermittelt werden können sowie den Namen der Array-Elemente, die für den jeweiligen Zugriff notwendig sind.

Array-Element Bedeutung
$_FILES['file']['name] Eigentlicher Name der Datei
$_FILES['file']['tmp_name'] Temporärer Name der Datei
$_FILES['file']['type'] MimeType der Datei
$_FILES['file']['size'] Größe der Datei
$_FILES['file']['error'] Möglicher aufgetretener Fehler beim Upload
Das Element $_FILES['file']['error'] liefert nur einen Fehlercode zurück.

Fehlerhandling im Skript

Mit einer einfachen if-Schleife können im Skript mögliche Fehler abgefangen und angezeigt werden. Eine Hilfe dabei ist, dass bei einem erfolgreichen Upload die Felder size und type zur Verfügung stehen, die in diesem Fall ausgegeben werden können. Der Beispielcode zeigt ein einfaches Fehlerhandling auf:

$size = $_FILES['file']['size'];
$type = $_FILES['file']['type'];

if(copy($tmp_filename, "D:/Webdav/test1/" . $filename) )
{
 $content = "Upload erfolgreich<br>Dateigröße: $size<br>
             Dateityp: $type ";
}
else 
{
 $content = "Kein Upload möglich; Ursache:" . 
            $_FILES['file']['error'];
}
echo $content;

Eine elegantere Methode, auf einen Fehler zu überprüfen, ist die Funktion is_uploaded_file. Die Funktion benötigt als Argument den temporären Namen der Upload-Datei (=> $_FILES). Bezogen auf den obigen Code muss die Zeile der if-Schleife entsprechend geändert werden. Der Rest des Quellcodes bleibt gleich. Das folgende Beispiel zeigt die Änderung:

if( is_uploaded_file($tmp_name) ) ...

Einsatz der Funktion move_uploaded_file()

Mit dieser Funktion kann eine vorher mit copy() hochgeladene Datei an eine bestimmte Stelle verschoben bzw. umbenannt werden. Bei dieser Funktion werden die gleichen Argumente für Quelle und Ziel wie bei copy() angegeben. Das bedeutet, dass die Quelle der temporäre Dateiname und das Ziel der eigentliche Dateiname inkl. der Zielpfad ist. Die Funktion überprüft vorher, ob die hochgeladene Datei gültig ist und führt dann den eigentlichen Vorgang aus. Ist die Datei ungültig, wird ein Fehler zurückgegeben.

Hinweis: Auch für move_uploaded_file() gilt das Prinzip, dass die Zieldatei überschrieben wird, wenn sie bereits vorhanden ist.

Upload von mehreren Dateien gleichzeitig

Im eigentlichen Formular muss nicht viel geändert werden. Zum einen muss für jede Datei ein eigenes <input type=file>-Feld angelegt werden. Dies kann statisch passieren, wenn man weiß, wie viele Dateien es sein sollen oder per Javascript, wenn die Anzahl nicht bekannt ist. Wichtig st der Name der der Felder. Dieser muss bei jeden Feld gleich sein und es muss zwingend ein "[]" hinzugefügt werden. Dadurch werden alle Felder in PHP automatisch einem Array hinzugefügt. Der Bereich der Eingabefelder sieht bei drei Dateien zum Upload nach der Änderung so aus:

<p>Bitte Datei 1 auswählen:<br>
<input type="file" name="file[]"></p>

<p>Bitte Datei 2 auswählen:<br>
<input type="file" name="file[]"></p>

<p>Bitte Datei 3 auswählen:<br>
<input type="file" name="file[]"></p>

Änderungen im PHP-Skript

Im Prinzip ändert sich an dem bisherigen Prinzip nichts. Allerdings muss bei mehreren Dateien zuerst die Anzahl für den Upload ermittelt werden und auf Basis dieser Information wird eine for-Schleife angelegt, über die jede Datei einzeln hochgeladen wird. Weiterhin muss noch eine Prüfroutine eingebaut werden, auf die im folgenden eingegangen wird.

$cnt = count($_FILES['file']['name']);

Diese Zeile ermittelt, wieviele Eingabefelder im HTML-Formular definiert wurden. Zu beachten ist dabei aber, dass immer die Gesamtanzahl angegeben wird, unabhängig davon, ob für eines der Felder eine Datei ausgewählt wurde oder nicht. Der Inhalt von $cnt ist gleichzeitig der Maximalwert für die noch folgende for-Schleife:

for($i=0; $i <= ($cnt-1); $i++)
{
 $tmp_filename = $_FILES['file']['tmp_name'][$i];
 $filename = $_FILES['file']['name'][$i];
 $size = $_FILES['file']['size'][$i];
 $type = $_FILES['file']['type'][$i];

 if( copy($tmp_filename, "D:/Webdav/test1/" . $filename) )
 {
  $content = "Upload erfolgreich<br>
              Dateigröße: $size<br>
              Dateityp: $type ";
 }
 else 
 {
  $content = "Kein Upload möglich; Ursache: " . 
             $_FILES['file']['error'][$i];
 }
 echo $content;
}

Die Elemente des $_FILES-Arrays stehen für jede einzelne Datei zur Verfügung. Allerdings muss innerhalb der for-Schleife noch ein [$i] hinzugefügt werden, um auch die Informationen für die Datei des aktuellen Schleifendurchlaufes zu bekommen. Die obige Schleife funktioniert einwandfrei, wenn für jedes Auswahlfeld eine Datei gewählt wurde. Wenn jetzt aber bei z.B. bei 4 Auswahlfeldern insgesamt nur 2 genutzt wurden, wird beim Durchlauf der Schleife zweimal eine Gutmeldung und zweimal eine Fehlermeldung ausgegeben, dass keine Datei ausgewählt wurde. Um dieses Problem noch zu lösen, muss man wissen, dass die Variablen $_FILES['file']['tmp_name'] oder $_FILES['file']['name'] für ein leeres Auswahlfeld leer sind. Auf diese Bedingung lässt sich in einer zusätzlichen if-Schleife zurückgreifen. Damit werden nur die Auswahlfelder abgearbeitet, für die auch eine Datei ausgewählt wurde. Mit dieser Erweiterung sieht der "copy-Part" der for-Schleife jetzt so aus:

if($tmp_filename != "")
{
 if( copy($tmp_filename, "D:/Webdav/test1/" . $filename) )
 {
  $content = "Upload erfolgreich<br>
              Dateigröße: $size<br>
              Dateityp: $type ";
 }
 else 
 {
  $content = "Kein Upload möglich; Ursache: " . 
              $_FILES['file']['error'][$i];
 }
 echo $content;
}

Über uns

Stefan Wienströer

Wir entwickeln Webanwendungen mit viel Leidenschaft. Unser Wissen geben wir dabei gerne weiter. Mehr über a coding project

Auch interessant