Ich versuche, eine Funktion zusammenzustellen, die einen Dateipfad empfängt, identifiziert, was er ist, die entsprechenden Header festlegt und ihn wie Apache bereitstellt.
Der Grund, warum ich dies tue, ist, dass ich PHP verwenden muss, um einige Informationen über die Anforderung zu verarbeiten, bevor ich die Datei bereitstelle.
Geschwindigkeit ist entscheidend
virtual () ist keine Option
Muss in einer gemeinsam genutzten Hosting-Umgebung funktionieren, in der der Benutzer keine Kontrolle über den Webserver hat (Apache / Nginx usw.)
Folgendes habe ich bisher:
File::output($path);
<?php
class File {
static function output($path) {
// Check if the file exists
if(!File::exists($path)) {
header('HTTP/1.0 404 Not Found');
exit();
}
// Set the content-type header
header('Content-Type: '.File::mimeType($path));
// Handle caching
$fileModificationTime = gmdate('D, d M Y H:i:s', File::modificationTime($path)).' GMT';
$headers = getallheaders();
if(isset($headers['If-Modified-Since']) && $headers['If-Modified-Since'] == $fileModificationTime) {
header('HTTP/1.1 304 Not Modified');
exit();
}
header('Last-Modified: '.$fileModificationTime);
// Read the file
readfile($path);
exit();
}
static function mimeType($path) {
preg_match("|\.([a-z0-9]{2,4})$|i", $path, $fileSuffix);
switch(strtolower($fileSuffix[1])) {
case 'js' :
return 'application/x-javascript';
case 'json' :
return 'application/json';
case 'jpg' :
case 'jpeg' :
case 'jpe' :
return 'image/jpg';
case 'png' :
case 'gif' :
case 'bmp' :
case 'tiff' :
return 'image/'.strtolower($fileSuffix[1]);
case 'css' :
return 'text/css';
case 'xml' :
return 'application/xml';
case 'doc' :
case 'docx' :
return 'application/msword';
case 'xls' :
case 'xlt' :
case 'xlm' :
case 'xld' :
case 'xla' :
case 'xlc' :
case 'xlw' :
case 'xll' :
return 'application/vnd.ms-excel';
case 'ppt' :
case 'pps' :
return 'application/vnd.ms-powerpoint';
case 'rtf' :
return 'application/rtf';
case 'pdf' :
return 'application/pdf';
case 'html' :
case 'htm' :
case 'php' :
return 'text/html';
case 'txt' :
return 'text/plain';
case 'mpeg' :
case 'mpg' :
case 'mpe' :
return 'video/mpeg';
case 'mp3' :
return 'audio/mpeg3';
case 'wav' :
return 'audio/wav';
case 'aiff' :
case 'aif' :
return 'audio/aiff';
case 'avi' :
return 'video/msvideo';
case 'wmv' :
return 'video/x-ms-wmv';
case 'mov' :
return 'video/quicktime';
case 'zip' :
return 'application/zip';
case 'tar' :
return 'application/x-tar';
case 'swf' :
return 'application/x-shockwave-flash';
default :
if(function_exists('mime_content_type')) {
$fileSuffix = mime_content_type($path);
}
return 'unknown/' . trim($fileSuffix[0], '.');
}
}
}
?>
php
performance
file-io
x-sendfile
Kirk Ouimet
quelle
quelle
$extension = end(explode(".", $pathToFile))
oder Sie können dies mit substr und strrpos tun :$extension = substr($pathToFile, strrpos($pathToFile, '.'))
. Als Ersatz fürmime_content_type()
können Sie auch einen Systemaufruf versuchen:$mimetype = exec("file -bi '$pathToFile'", $output);
Antworten:
Meine vorherige Antwort war teilweise und nicht gut dokumentiert. Hier ist ein Update mit einer Zusammenfassung der Lösungen daraus und von anderen in der Diskussion.
Die Lösungen sind von der besten bis zur schlechtesten Lösung geordnet, aber auch von der Lösung, die die meiste Kontrolle über den Webserver benötigt, bis zu der Lösung, die die weniger benötigt. Es scheint keine einfache Möglichkeit zu geben, eine Lösung zu finden, die sowohl schnell ist als auch überall funktioniert.
Verwenden des X-SendFile-Headers
Wie von anderen dokumentiert, ist es tatsächlich der beste Weg. Die Basis ist, dass Sie Ihre Zugriffskontrolle in PHP durchführen und dann, anstatt die Datei selbst zu senden, den Webserver anweisen, dies zu tun.
Der grundlegende PHP-Code lautet:
Wo
$file_name
ist der vollständige Pfad im Dateisystem?Das Hauptproblem bei dieser Lösung besteht darin, dass sie vom Webserver zugelassen werden muss und entweder nicht standardmäßig installiert ist (Apache), standardmäßig nicht aktiv ist (lighttpd) oder eine bestimmte Konfiguration benötigt (nginx).
Apache
Wenn Sie unter apache mod_php verwenden, müssen Sie ein Modul namens mod_xsendfile installieren und dann konfigurieren (entweder in apache config oder .htaccess, wenn Sie dies zulassen).
Mit diesem Modul kann der Dateipfad entweder absolut oder relativ zum angegebenen sein
XSendFilePath
.Lighttpd
Das mod_fastcgi unterstützt dies, wenn es mit konfiguriert wird
Die Dokumentation für die Funktion befindet sich im lighttpd-Wiki. Sie dokumentiert den
X-LIGHTTPD-send-file
Header, aber derX-Sendfile
Name funktioniert auchNginx
Unter Nginx können Sie den
X-Sendfile
Header nicht verwenden. Sie müssen einen eigenen Header mit dem Namen verwendenX-Accel-Redirect
. Es ist standardmäßig aktiviert und der einzige wirkliche Unterschied besteht darin, dass das Argument ein URI und kein Dateisystem sein sollte. Die Folge ist, dass Sie einen Speicherort definieren müssen, der in Ihrer Konfiguration als intern markiert ist, um zu vermeiden, dass Clients die echte Datei-URL finden und direkt darauf zugreifen. Ihr Wiki enthält eine gute Erklärung dafür.Symlinks und Standort-Header
Sie können Symlinks verwenden und zu diesen umleiten. Erstellen Sie einfach Symlinks zu Ihrer Datei mit zufälligen Namen, wenn ein Benutzer berechtigt ist, auf eine Datei zuzugreifen, und leiten Sie den Benutzer mit folgenden Methoden zu dieser Datei um.
Natürlich benötigen Sie eine Möglichkeit, sie entweder beim Aufruf des Skripts zum Erstellen oder über cron (auf dem Computer, wenn Sie Zugriff haben, oder über einen anderen Webcron-Dienst) zu bereinigen.
Unter Apache müssen Sie in der Lage sein,
FollowSymLinks
in einer.htaccess
oder in der Apache-Konfiguration zu aktivieren .Zugriffskontrolle über IP und Location Header
Ein weiterer Hack besteht darin, Apache-Zugriffsdateien aus PHP zu generieren, die die explizite Benutzer-IP zulassen. Unter Apache bedeutet es,
mod_authz_host
(mod_access
)Allow from
Befehle zu verwenden.Das Problem ist, dass das Sperren des Zugriffs auf die Datei (da mehrere Benutzer dies gleichzeitig tun möchten) nicht trivial ist und dazu führen kann, dass einige Benutzer lange warten. Und Sie müssen die Datei trotzdem beschneiden.
Offensichtlich wäre ein weiteres Problem, dass mehrere Personen hinter derselben IP möglicherweise auf die Datei zugreifen könnten.
Wenn alles andere fehlschlägt
Wenn Sie wirklich keine Möglichkeit haben, Ihren Webserver dazu zu bringen, Ihnen zu helfen, ist die einzige verbleibende Lösung das Readfile , das in allen derzeit verwendeten PHP-Versionen verfügbar ist und ziemlich gut funktioniert (aber nicht wirklich effizient ist).
Lösungen kombinieren
Der beste Weg, um eine Datei sehr schnell zu senden, wenn Sie möchten, dass Ihr PHP-Code überall verwendet werden kann, besteht darin, irgendwo eine konfigurierbare Option zu haben, die Anweisungen zur Aktivierung in Abhängigkeit vom Webserver und möglicherweise eine automatische Erkennung in Ihrer Installation enthält Skript.
Es ist ziemlich ähnlich zu dem, was in einer Menge Software für gemacht wird
mod_rewrite
auf Apache)mcrypt
PHP-Modul)mbstring
PHP Modul)quelle
header("Location: " . $path);
?Der schnellste Weg: Nicht. Schauen Sie in den x-sendfile-Header für nginx , es gibt ähnliche Dinge auch für andere Webserver. Dies bedeutet, dass Sie weiterhin Zugriffskontrolle usw. in PHP durchführen können, aber das eigentliche Senden der Datei an einen dafür vorgesehenen Webserver delegieren können.
PS: Ich bekomme Schüttelfrost, wenn ich nur daran denke, wie viel effizienter dies mit Nginx ist, verglichen mit dem Lesen und Senden der Datei in PHP. Stellen Sie sich vor, 100 Leute laden eine Datei herunter: Mit PHP + Apache, großzügig, das sind wahrscheinlich 100 * 15 MB = 1,5 GB (ungefähr, erschießen Sie mich) RAM genau dort. Nginx gibt die Datei einfach an den Kernel weiter und lädt sie dann direkt von der Festplatte in die Netzwerkpuffer. Schnell!
PPS: Und mit dieser Methode können Sie immer noch alle gewünschten Zugriffskontroll- und Datenbankaufgaben erledigen.
quelle
readfile()
Hier geht eine reine PHP-Lösung. Ich habe die folgende Funktion aus meinem persönlichen Rahmen angepasst :
Der Code ist so effizient wie möglich. Er schließt den Sitzungshandler, sodass andere PHP-Skripte gleichzeitig für denselben Benutzer / dieselbe Sitzung ausgeführt werden können. Es unterstützt auch das Bereitstellen von Downloads in Bereichen (was Apache vermutlich auch standardmäßig tut), sodass Benutzer Downloads pausieren / fortsetzen und von höheren Download-Geschwindigkeiten mit Download-Beschleunigern profitieren können. Außerdem können Sie die maximale Geschwindigkeit (in Kbit / s) angeben, mit der der Download (Teil) über das
$speed
Argument bereitgestellt werden soll .quelle
eio
ist auch nicht immer verfügbar. Trotzdem wusste +1 nichts von dieser Pecl-Erweiterung. =)$size = sprintf('%u', filesize($path))
?Lassen Sie Apache die Arbeit für Sie erledigen.
quelle
Eine bessere Implementierung mit Cache-Unterstützung und angepassten http-Headern.
quelle
Wenn Sie die Möglichkeit haben, PECL-Erweiterungen zu Ihrem PHP hinzuzufügen, können Sie einfach die Funktionen aus dem Fileinfo-Paket verwenden , um den Inhaltstyp zu bestimmen und dann die richtigen Header zu senden ...
quelle
Die
Download
hier erwähnte PHP- Funktion verursachte einige Verzögerungen, bevor die Datei tatsächlich heruntergeladen wurde. Ich weiß nicht , ob dies durch die Verwendung Lack - Cache verursacht wurde oder was, aber für mich half es die entfernensleep(1);
vollständig und Satz$speed
zu1024
. Jetzt funktioniert es ohne Probleme, so schnell wie die Hölle. Vielleicht können Sie diese Funktion auch ändern, weil ich gesehen habe, dass sie im gesamten Internet verwendet wird.quelle
Ich habe eine sehr einfache Funktion codiert, um Dateien mit PHP und automatischer MIME-Typerkennung bereitzustellen:
Verwendung
quelle