[PHP] Dateiupload und Dateidownload

Jeder macht das eine und einige machen auch das andere. Die Rede ist vom Hoch- und Runterladen von Dateien im Internet. Jeder kann etwas herunter laden. Ob die Bedienungsanleitung, im PDF-Format, für den Fernseher oder ein schickes Bildchen als Bildschirmhintergrund oder oder oder… die Möglichkeiten sind schier unbegrenzt. Auf der anderen Seite gibt es natürlich auch I-Net Nutzer die auch Dinge hochladen.

Hier möchte ich kurz erklären wie man es als PHP Programmierer lösen kann. Viele Wege führen bekanntlich nach Rom. Hier mein Weg.
Als erstes der Upload. Hier brauchen wir natürlich überhaupt erst einmal eine HTML-Form mit einem Dateifeld. In seiner einfachsten Form würde das so aussehen:

<form enctype="multipart/form-data" method="post">
<input name="file" type="file" />
<input name="Submit" type="submit" value="Senden" />
</form>

Hier kann der User dann seine Datei auswählen und die Form abschicken. Damit wird, in diesem Fall, die Datei erneut aufgerufen und die Formulardaten mit übergeben. Sprich die Dateidaten. Diese können nun mit PHP erfasst und ausgewertet werden.

if(!empty($_FILES["file"]["name"])) {
	$UploadFolder = "./uploads/";

	//Dateinamen auf ungueltige Zeichen pruefen
	$Dateiname = preg_replace('/[^a-z0-9.-_]/', '', strtolower($_FILES["file"]["name"]));

	//Existiert die Datei auch noch nicht
	if(!file_exists($UploadFolder.$Dateiname)) {
		//An die richtige Stelle kopieren
		move_uploaded_file($_FILES['file']['tmp_name'], $UploadFolder.$Dateiname);
		if(!file_exists($UploadFolder.$Dateiname)) {
			echo "Datei konnte nicht hochgeladen werden. Bitte wenden Sie sich an einen Administrator.";
		} else {
			//Datei ist hochgeladen
		}
	} else {
		echo "Es existiert bereits eine Datei mit diesem Dateinamen. Bitte bennenen Sie die Datei um.";
	}
}

So. Damit haben wir die Datei hochgeladen und in dem Ordner „Uploads“ abgelegt. Das ist natürlich nicht alles. Jeder muss hier seinen eigenen Weg gehen.
Der Dateiname muss z.B. irgendwo (Datenbank) gespeichert werden, damit sich das System später daran „erinnert“. Aber für den Upload selbst reicht das.

Kommen wir zum 2. Teil. Dem Download der Datei.

Ein User hat jetzt eine Bedienungsanleitung auf einer Webseite hochgeladen und für andere Benutzer zum Download zur Verfügung gestellt. Jetzt kommt ein anderer User und möchte die Datei downloaden.

Als Programmierer wäre es das einfachste einfach einen Link zu veröffentlichen der auf die Datei, im Dateisystem, zeigt. Das hätte aber den Nachteil das dieser Link kopiert werden könnte und jeder die Datei einfach herunterladen kann. Schlecht wenn das vielleicht eigentlich nur registrierte Mitglieder dürfen sollen.
Also muss eine andere Lösung her. Hier wieder mein Weg nach Rom ;-)

$UploadFolder = "./uploads/"; //Name des Ordners mit der Downloaddatei
$FileName = "musterdatei.pdf"; //Name der Downloaddatei

header("Content-Type: ".$mimeType);
header("Content-Length: ".filesize($UploadFolder.$FileName));
header("Content-Disposition: attachment; filename="".$FileName.""");
readfile($UploadFolder.$FileName);
exit;

Ja, das war es schon ;-) Aber es ist etwas komplizierter als es auf den ersten Blick scheint. Ich denke die ersten beiden Variablen sind klar. Der Content-Type beschreibt die Datei. Ob es ein Bild, PDF, Word-Dokument oder sonst was ist. Am besten ist hier diesen beim Upload gleich mit zu speichern. Denn das ist der einzige Zeitpunkt wo Du den Content Type „geliefert“ bekommst. Sonst könnte man ihn noch über die Dateierweiterung rausbekommen.

Content-Length ist die Länge der Datei. Vorsicht, nicht wirklich die Größe. Da 1 Zeichen aber einem Byte entspricht – wie praktisch – können wir einfach die Dateigröße in Byte (!) angeben. ;-)

Bei Content-Disposition wird einfach gesagt Dateianhang und den Dateinamen. Hier muss nicht der Originale Dateiname angegeben werden, es könnte auch ein fiktiver angegeben werden.

Und zu guter letzt noch mit readfile die Datei einlesen und gleichzeitig ausgeben.

Ganz wichtig ist hier noch das vor diesem Code keine Ausgabe an den Browser gemacht werden darf. Am besten ist es für den Download eine eigene Datei zu erstellen und vor diesem Code nur das nötigste zu tun. Zum Beispiel checken ob der User, der den Download starten möchte, auch die Berechtigung hat oder die Variablen aus einer Datenbank auslesen. Aber keinerlei ausgaben an den Browser machen, sonst erscheinen ganz viele komische Zeichen im Browser ;-)

6 Antworten
  1. anonym
    anonym says:

    wieso hast du keinen submit button gemacht beim upload?

    und könntest du bitte genauer erläutern wie man die beiden scripts in die datenbank einbindet?

    Antworten
  2. Gordon
    Gordon says:

    Hi.

    Sorry, ich dachte das ist klar bei einer Form. Ok, könnte man auch per Javascript absenden. So wieder jeder es mag ;-) Hab aber trotzdem im Codesnipsel oben jetzt einen Button eingefügt.

    Naja, Du brauchst zumindest eine Datenbanktabelle. Hier würde ich folgende Felder vorschlagen. ID, Dateiname, MIME. Die ID ist eine fortlaufende Nummer. Den MIME Typ erhälst Du beim Upload mit im $_FILES. Diesen später Auszulesen bzw. zu bestimmen wird komplizierter und warum kompliziert machen wenn er schon geliefert wird.
    Beim Upload speicherst Du die Datei ja in „$UploadFolder.$Dateiname“. $Dateiname ist das was Du in die Datenbank in „Dateiname“ speicherst. Den Pfad kennst Du ja. Und willst Du später mal die Dateien in ein anderes Verzeichnis kopieren, so musst Du nicht alle Datenbankeinträge bearbeiten. Auch wenn das vielleicht nie nötig ist, aber man weiß ja nie.
    Soweit der Upload und das Speichern. Der Download geht dann den umgekehrten Weg. Du suchst in der Datenbank nach der entsprechenden Datei (hoffentlich mit der ID… geht schneller und dafür ist sie da). Liest den Dateinamen aus und übergibst ihn an die Variable „$FileName“. Den MIME Typ an die Variable „$mimeType“ und das war es.

    Das ist natürlich sehr spartanisch jetzt. In der Regel besteht ja ein Script aus mehr als nur Up- und Download. Aber das ist alles was Du für den Up- und Download selbst brauchst.

    Antworten
  3. Torben Leuschner
    Torben Leuschner says:

    Hey Gordon,

    ungefähr den gleichen Upload verwende ich auch häufiger.

    Kleiner Tipp:
    Ein Syntax-Highlighting-Plugin würde dem Quellcodes in deinem Blog gut tun :)
    Weitermachen!

    Herzliche Grüße,
    Torben Leuschner

    Antworten
  4. maik
    maik says:

    Hey, gordon.
    Ich brauche den download in meinen shop.
    Es sind dort alles Zip Dateien die zum download bereit stehen. ich verwende als attachment/zip.
    allerdings bekomme ich nie die richtige file…
    kannst du mir sagen was nihct stimmt?

    $uploadFolder = WWW_ROOT.“model/“;
    $fileName = ‚6d41458ffd623de3f14b476e86b4a6e9test‘;
    //var_dump($uploadFolder.$fileName);
    header(‚Content-Type: application/zip‘);
    header(‚Content-Length:‘.filesize($uploadFolder.$fileName));
    header(„Content-Disposition: attachment; filename=““.$filename.“““);
    readfile($uploadFolder.$fileName);

    exit;

    Antworten
  5. Gordon
    Gordon says:

    Hallo Maik,

    hmm… alles was mir daran aktuell auffällt ist die vorletzte Zeile:
    header(“Content-Disposition: attachment; filename=\”“.$filename.”\”“);

    $filename != $fileName

    Wenn es das nicht war, dann sag noch mal bescheid. Lasse dir evtl. Ganz unten noch mal die Variablen ausgeben. Kann ja nur was mit der Filename oder Folder Variable zu tun haben.

    Viele Grüße
    Gordon

    PS: „Das richtige File“ hat nicht immer was mit dem Dateinamen zu tun. Hast Du den Dateiinhalt mal kontrolliert?

    Antworten

Hinterlassen Sie einen Kommentar

Wollen Sie an der Diskussion teilnehmen?
Feel free to contribute!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*