de.comp.lang.php.* FAQ

13.3. Wie realisiere ich einen Dateidownload mit PHP?

Keywords: Datei | Download | Header | MIME-Typ

Antwort von Kristian Köhntopp

Grundsätzlich kann man einen Dateidownload auf zwei verschiedene Arten realisieren: Entweder man schreibt ein PHP-Script, das einen Redirect (siehe Wie erzeuge ich mit PHP einen Redirect auf eine andere Seite?) auf die zu ladende Datei generiert, oder man startet den Download durch das PHP-Script. Die Methode mit dem Redirect hat den Nachteil, dass Anwender die Ziel-URL des Redirect mitbekommen und später dann direkt und ungeschützt auf diese Datei zugreifen können.

Will man das verhindern, muss man den Download innerhalb von PHP abhandeln. Die zu ladenden Dateien liegen dann außerhalb der Document Root des Webservers (haben also keine URL) und sind nur durch PHP zugreifbar. In PHP sendet man den passenden MIME-Typ als Header und schickt dann die gewünschte Datei hinterher. Natürlich kann man vorher noch einen Downloadzähler aktualisieren oder überprüfen, ob der Anwender überhaupt für den Download autorisiert ist.

// $download sei der Bezeichner für die zu ladende Datei
// etwa: 
$download = $_GET['download'];

// Dieses Verzeichnis liegt außerhalb des Document Root und
// ist nicht per URL erreichbar.
$basedir = "/home/www/download";

// Übersetzung von Download-Bezeichner in Dateinamen.
$filelist = array(
  "file1" => "area1/datei1.zip",
  "file2" => "area1/datei2.zip",
  "file3" => "area2/datei1.zip"
);

// Einbruchsversuch abfangen.
if (!isset($filelist[$download]))
  die("Datei $download nicht vorhanden.");

// Vertrauenswürdigen Dateinamen basteln.
$filename = sprintf("%s/%s", $basedir, $filelist[$download]);

// Passenden Datentyp erzeugen.
header("Content-Type: application/octet-stream");

// Passenden Dateinamen im Download-Requester vorgeben,
// z. B. den Original-Dateinamen
$save_as_name = basename($filelist[$download]);
header("Content-Disposition: attachment; filename=\"$save_as_name\"");

// Datei ausgeben.
readfile($filename);

Dieses Script kann mit dem Parameter $download aufgerufen werden. Dieser Parameter wird dann in den Namen einer zu ladenden Datei übersetzt. Aus Sicherheitsgründen ist es nicht möglich, dem Script direkt Dateinamen zu übergeben - wir möchten vermeiden, dass jemand als Parameter download=../../../../../../../etc/passwd oder ähnliche Namen erfolgreich übergeben kann. Die Prüfung ist eine Variante des ersten Prüfschemas aus Wie unterscheide ich böse Variablen von guten?.

Antwort von Guido Haeger

Die Reaktion des UserAgent auf die oben genannten Header ist in den RFCs für HTTP und MIME nicht eindeutig definiert. Zusätzlich haben einige Browser Bugs bezüglich der korrekten Interpretation von Content-Disposition-Headern. Es wird dann häufig versucht, den UserAgent mit nicht standardisierten Header-Kreationen zum Anzeigen des "Speichern Unter"-Dialoges zu zwingen. Das mag bei einigen UserAgents rein zufällig zum gewünschten Verhalten führen, ist aber nicht empfehlenswert. Sofern nicht standardisierte Content-Type-Header verwendet werden, so sind diese mit einem x- einzuleiten (Content-Type: x-type/x-subtype).

Der Internet Explorer 5.5 ignoriert in vielen Fällen das Filename-Attribut des Content-Disposition-Headers und schlägt vor, die Datei nicht unter dem dort vorgegebenen Dateinamen, sondern unter dem Namen des PHP-Scriptes zu speichern. Für dieses Problem gibt es verschiedene Workarounds. Bei Verwendung des Apache als HTTP-Server kann man z.B. ModRewrite verwenden. In die .htaccess oder http.conf:

RewriteEngine on
RewriteRule download/([^/]{1,30})\.([a-z0-9]{2,4}) /download.php?filename=$1&extension=$2

Bei einem Request für /download/prospekt.pdf wird auf dem Server dann das Script /download.php?filename=prospekt&lextension=pdf aufgerufen. Wenn der Internet Explorer 5.5 das Filename-Attribut des Content-Disposition-Headers ignoriert, so wird er den String hinter dem letzten Slash des URL als Dateinamen für die zu speichernde Datei anbieten. In diesem Beispiel also prospekt.pdf.

Valid HTML 4.01! Valid CSS!

13.3. Wie realisiere ich einen Dateidownload mit PHP?
http://www.php-faq.de/q/q-datei-download.html
Archiv der de.comp.lang.php-FAQ Dies ist eine Archivseite von 2008 und wurde seitdem nicht geändert. Das dclp-FAQ-Team