Ein recht verstecktes Feature, das neuere Server, bspw. Lighthttpd + FCGI-Modul bereits “out of the box” unterstützen, ist das anwendungsgesteuerte “Durchschleifen” von großen Dateien via X-Sendfile Header.
Wozu es gut ist und wie man es benutzt,will ich im Folgenden kurz erläutern:
Im Prinzip dasseselbe wie fpassthru(), aber eben ohne dass PHP den auszuliefernden Datenstrom komplett in den Speicher laden muss. Somit verbindet X-Sendfile die beiden Vorteile, dass bspw. Zugriffsberechtigungen applikationsseitig via PHP geprüft werden können und dass die Response selbst ausschließlich durch den darunterliegenden Server (in dem Fall Apache 2) ausliefert wird (und PHP komplett umgeht).
Somit ist es möglich, das PHP-eigene memory_limit zu umgehen und auch riesige Dateien zum (PHP-geschützten) Download bereitzustellen.
Für den Apachen gibt es zur Unterstützung dieses Features mod_xsendfile. Das Modul ist leider nicht in den offiziellen Ubuntu-Paketquellen enthalten, und auch dotdeb konnte mir bei meiner Suche nicht weiterhelfen. Die Übersetzung + Installation via apxs2 läuft aber unter Ubuntu problemlos (apxs2 ist jeweils in den Paketen apache2-prefork-dev und apache2-threaded-dev enthalten). Standard unter Ubuntu oder generell Gnu/Linux sollte “Prefork” sein.
apxs -cia mod_xsendfile.c
kompiliert das Modul und installiert es nach /usr/lib/apache2/modules. Anschließend erstellt man eine neue Konfigurationsdatei unter /etc/apache2/mods-available/mod_xsendfile.load mit folgendem Inhalt:
LoadModule xsendfile_module /usr/lib/apache2/modules/mod_xsendfile.so <Files *.php> XSendFile on XSendFileAllowAbove on </Files>
Dann das Modul noch mit
$ a2enmod xsendfile
aktivieren.
So hats bei mir auf Anhieb funktioniert, zu den einzelnen Einstellungsmöglichkeiten schaut man dann einfach nochmal in die Doku.
Das PHP-Script sieht dann exemplarisch folgendermaßen aus:
if($user->isAuthenticated()) { // Wir werden eine Binär-Datei ausgeben header('Content-type: application/octet-stream'); // Es wird downloaded.pdf benannt header('Content-Disposition: attachment; filename="'.$file->getFilename().'"'); header('Content-Length: ' . $file->getSize()); // PASS X-SENDFILE-HEADER. header('X-Sendfile: ' . $file->getPathname()); }
Das war einfach, oder?
ja, und sehr nützliche Sache.
bedankt!