6.4 HTTP_Download 

Besprochene Version: 1.0.0RC1 | Lizenz: PHP |
Klassendatei(en): HTTP/Download.php |
HTTP_Download unterstützt Sie dabei, Daten an einen Client zu versenden. Genauer gesagt geht es darum, (Binär-)Daten per PHP an einen Client zu schicken, so dass dieser davon ausgeht, dass es sich um einen bestimmten Dateityp handelt. Das ist immer dann hilfreich, wenn Sie z. B. einen Browser davon abhalten wollen, eine PDF-Datei direkt zu öffnen, oder Bilddateien in einer Datenbank abgelegt haben und an einen Browser versenden wollen. Darüber hinaus können Sie auf Basis dieser Klasse z. B. den Download von Dateien mit einem Passwort sichern, die Bandbreite für den Download beschränken oder die Daten komprimieren.
Möchten Sie eine Datei mithilfe des Pakets versenden, können Sie das im einfachsten Fall mit der Methode staticSend() machen. Sie wird, wie der Name schon vermuten lässt, statisch aufgerufen und schickt entweder eine Datei oder Daten aus einer Variable direkt zum Client. Die Methode bekommt ein assoziatives Array mit allen notwendigen Informationen übergeben.
Zum Übergeben der zu versendenden Daten stehen drei Möglichkeiten zur Verfügung. Um den Pfad zu einer Datei zu übergeben, die versendet werden soll, nutzen Sie den Array-Schlüssel file. Ist die Datei bereits zum Lesen geöffnet und können Sie auf einen entsprechenden Resource Identifier zurückgreifen, so können Sie diesen als Wert des Schlüssels resource übergeben. Die dritte Möglichkeit ist, den Schlüssel data zu nutzen und die Daten direkt an die Methode zu übergeben. Das bietet sich immer dann an, wenn die Daten direkt durch Ihr Programm generiert wurden. Wichtig ist, dass Sie nur eine dieser Möglichkeiten nutzen, da es nicht möglich ist, zwei Dateien auf einmal zu senden.
Darüber hinaus können Sie noch sieben weitere Felder in dem Array belegen, um den Download zu steuern. Der Schlüssel gzip unterstützt einen booleschen Wert, mit dem Sie festlegen können, ob die Datei für die Übertragung komprimiert werden soll. Dies bezieht sich nur auf den Übertragungsweg, da der Browser die Datei beim Empfang sofort wieder dekomprimiert. Somit wird der Dateiname auch nicht verändert. Um diese Komprimierung nutzen zu können, muss allerdings die zlib-Bibliothek verfügbar sein. Ist das nicht der Fall, resultiert der Aufruf der Methode in einem PEAR_Error-Objekt. Somit ist die Kompression standardmäßig auch ausgeschaltet.
Um das Caching-Verhalten des Clients zu steuern, steht der Schlüssel cache zur Verfügung. Übergeben Sie hier ein false – der Standard ist true –, so werden Header gesendet, die ein Zwischenspeichern des Downloads verhindern sollen. Der Schlüssel lastmodified dient dazu, das Datum der letzten Änderung der Datei als Unix-Timestamp zu übergeben. Daran kann ein Client erkennen, wie aktuell die Datei ist. Übergeben Sie die zu sendenden Daten als Datei, so ermittelt die Methode selbstständig das letzte Änderungsdatum der Datei. Die Information, was dem Client geschickt wird und wie er damit umgehen soll, können Sie mit den nächsten beiden Schlüsseln definieren. Mit contenttype können Sie einen der MIME-Types übergeben, die in Tabelle 6.2 angegeben sind. Geben Sie keinen MIME-Type an, nutzt das Paket standardmäßig application/x-octetstream. Das teilt dem Client mit, dass es sich um Binärdaten handelt, was üblicherweise dazu führt, dass der Browser anbietet, die Datei abzuspeichern.
Die Information, wie der Client mit der Datei umgehen soll, wird mit dem Schlüssel contentdisposition definiert. An dieser Stelle ist allerdings ein numerisches Array zu übergeben, das aus zwei Elementen besteht. Das erste Element kann eine der Konstanten HTTP_DOWNLOAD_ATTACHMENT oder HTTP_DOWNLOAD_INLINE sein. Die erste Konstante informiert den Client darüber, dass die gesendeten Daten auf der Festplatte abgespeichert werden sollen. Die zweite weist ihn an, die Daten – wenn möglich – sofort darzustellen. Der zweite Parameter dieses verschachtelten Arrays ist der Dateiname, unter dem die Datei abgespeichert werden soll. Das ist natürlich nur dann relevant, wenn diese wirklich auf der Festplatte abgelegt werden soll oder wenn Sie davon ausgehen können, dass der Client die Datei eventuell nicht darstellen kann, weil ein dazu notwendiges Programm nicht vorhanden ist.
<?php require_once("HTTP/Download.php"); $params = array( 'file' => 'temp.tmp', // einzulesende Datei 'contenttype' => 'text/rtf', // Versenden als RTF-Datei 'contentdisposition' => array( HTTP_DOWNLOAD_ATTACHMENT, // Download 'Daten.rtf' // Speichern als Daten.rtf ), ); $fehler = HTTP_Download::staticSend($params); if (PEAR::isError($fehler)) { echo $fehler->getMessage(); } ?>
Listing 6.4 Versenden einer Datei mit HTTP_Download
Die Methode staticSend() bekommt das besprochene Array übergeben und versucht, die Datei zu senden. Ist das nicht möglich, weil die Datei beispielsweise nicht gefunden wurde oder weil ein anderes Problem auftaucht, liefert sie ein PEAR_Error-Objekt zurück. Bitte beachten Sie, dass das Paket voraussetzt, dass noch keine Header an den Client versandt wurden. Somit dürfen also auch noch keine HTML-Tags oder auch nur eine Leerzeile verschickt worden sein.
Zwei Elemente des Parameter-Arrays habe ich noch nicht erwähnt. Mit ihnen können Sie die Download-Geschwindigkeit steuern. Das Array-Element buffersize bestimmt, wie viele Bytes auf einmal übertragen werden, und mit throttledelay können Sie definieren, wie viele Sekunden zwischen den einzelnen Datenpaketen pausiert werden soll. Eine kleinere Angabe als eine Sekunde ist leider nicht möglich.
$params = array( 'file' => 'temp.tmp', 'contenttype'=> 'text/rtf', 'contentdisposition' => array(HTTP_DOWNLOAD_ATTACHMENT, 'Daten.rtf'), 'buffersize'=>(10*1024), //Chunk-Groesse 10 KB 'throttledelay'=>1 // 1 Sekunde Pause nach jedem Chunk );
Mit diesen Parametern würde also die Datei temp.tmp eingelesen und in Blöcken zu je 10 KB an den Client gesendet, wobei nach jedem Block eine Pause von einer Sekunde eingelegt wird.
Die Nutzung der Methode staticSend() erscheint in meinen Augen recht sinnvoll. Zwar können Sie auch erst ein Objekt ableiten und die einzelnen Parameter dann mithilfe von dedizierten Methoden setzen. Aber das erscheint deutlich aufwändiger und bietet keine echten Vorteile.
Eine zweite statische Methode, die in dem Paket definiert und sehr interessant ist, ist sendArchive(). Zwar ist sie nicht so flexibel wie staticSend(), aber sie gibt Ihnen die Möglichkeit, eine Datei oder ein Unterverzeichnis »on the fly« zu packen und zu versenden. Als ersten Parameter bekommt die Methode den Namen der Datei übergeben, unter dem sie an den Client geschickt werden soll. Der zweite Parameter ist der Name der Datei oder des Unterverzeichnisses, das komprimiert werden soll. Danach folgt eine Konstante, die definiert, mit welchem Verfahren archiviert bzw. gepackt werden soll. Zur Verfügung stehen:
- HTTP_ARCHIVE_ZIP, um ein ZIP-Archiv zu generieren.
Die ersten drei Konstanten setzen voraus, dass Archive_Tar installiert ist, wohingegen die letzte Archive_Zip benötigt.
Nachdem Sie definiert haben, wie das Archiv zu erstellen ist, können Sie noch festlegen, ob den zu komprimierenden Dateien ein Pfad vorangestellt werden soll oder ob ein bereits vorhandener Pfad gelöscht werden soll.
require_once("HTTP/Download.php"); $fehler= HTTP_Download::sendArchive( 'archiv.tgz', // Dateiname beim Client '/var/www/daten', // Verzeichnis zum Packen HTTP_DOWNLOAD_TGZ, // Methode zum Komprimieren '/backup', // Pfad, der vorangestellt wird '/var/www' // Pfad, der entfernt wird ); if (true===PEAR::isError($fehler)) { die ($fehler->getMessage()); }
Bitte beachten Sie, dass das Packen von ganzen Verzeichnissen recht zeitintensiv sein kann. Somit kann es schnell passieren, dass die 30 Sekunden, die ein Script üblicherweise laufen darf, schnell überschritten werden. Sollte das ein Problem sein, könnte set_time_limit() Ihnen weiterhelfen.