J'essaie de créer une fonction qui reçoit un chemin de fichier, identifie ce que c'est, définit les en-têtes appropriés et la sert comme le ferait Apache.
La raison pour laquelle je fais cela est que je dois utiliser PHP pour traiter certaines informations sur la demande avant de servir le fichier.
La vitesse est essentielle
virtual () n'est pas une option
Doit travailler dans un environnement d'hébergement partagé où l'utilisateur n'a aucun contrôle sur le serveur Web (Apache / nginx, etc.)
Voici ce que j'ai jusqu'à présent:
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
la source
la source
$extension = end(explode(".", $pathToFile))
ou vous pouvez le faire avec substr et strrpos:$extension = substr($pathToFile, strrpos($pathToFile, '.'))
.mime_content_type()
Vous pouvez également essayer un appel système en guise de solution de secours :$mimetype = exec("file -bi '$pathToFile'", $output);
Réponses:
Ma réponse précédente était partielle et mal documentée, voici une mise à jour avec un résumé des solutions de celle-ci et des autres dans la discussion.
Les solutions sont classées de la meilleure solution à la pire, mais aussi de la solution nécessitant le plus de contrôle sur le serveur Web à celle nécessitant le moins. Il ne semble pas y avoir de moyen simple d'avoir une solution à la fois rapide et efficace partout.
Utilisation de l'en-tête X-SendFile
Tel que documenté par d'autres, c'est en fait le meilleur moyen. La base est que vous effectuez votre contrôle d'accès en php, puis au lieu d'envoyer le fichier vous-même, vous dites au serveur Web de le faire.
Le code PHP de base est:
Où
$file_name
est le chemin d'accès complet sur le système de fichiers.Le principal problème avec cette solution est qu'elle doit être autorisée par le serveur Web et qu'elle n'est pas installée par défaut (apache), n'est pas active par défaut (lighttpd) ou nécessite une configuration spécifique (nginx).
Apache
Sous apache si vous utilisez mod_php vous devez installer un module appelé mod_xsendfile puis le configurer (soit dans apache config ou .htaccess si vous l'autorisez)
Avec ce module, le chemin du fichier peut être absolu ou relatif à celui spécifié
XSendFilePath
.Lighttpd
Le mod_fastcgi prend en charge cela lorsqu'il est configuré avec
La documentation de la fonctionnalité se trouve sur le wiki lighttpd, ils documentent l'en-
X-LIGHTTPD-send-file
tête mais leX-Sendfile
nom fonctionne égalementNginx
Sur Nginx, vous ne pouvez pas utiliser l'en-
X-Sendfile
tête, vous devez utiliser leur propre en-tête nomméX-Accel-Redirect
. Il est activé par défaut et la seule vraie différence est que son argument doit être un URI et non un système de fichiers. La conséquence est que vous devez définir un emplacement marqué comme interne dans votre configuration pour éviter que les clients trouvent l'URL réelle du fichier et y accèdent directement, leur wiki en contient une bonne explication .En-tête de liens symboliques et d'emplacement
Vous pouvez utiliser des liens symboliques et les rediriger, créez simplement des liens symboliques vers votre fichier avec des noms aléatoires lorsqu'un utilisateur est autorisé à accéder à un fichier et rediriger l'utilisateur vers celui-ci en utilisant:
Évidemment, vous aurez besoin d'un moyen de les élaguer soit lorsque le script pour les créer est appelé, soit via cron (sur la machine si vous avez accès ou via un service webcron sinon)
Sous apache, vous devez pouvoir activer
FollowSymLinks
dans un.htaccess
ou dans la configuration apache.Contrôle d'accès par IP et en-tête de localisation
Un autre hack consiste à générer des fichiers d'accès apache à partir de php permettant l'IP de l'utilisateur explicite. Sous Apache, cela signifie utiliser les commandes
mod_authz_host
(mod_access
)Allow from
.Le problème est que le verrouillage de l'accès au fichier (car plusieurs utilisateurs peuvent vouloir le faire en même temps) n'est pas trivial et peut amener certains utilisateurs à attendre longtemps. Et vous devez quand même élaguer le fichier.
De toute évidence, un autre problème serait que plusieurs personnes derrière la même adresse IP pourraient potentiellement accéder au fichier.
Quand tout le reste échoue
Si vous n'avez vraiment aucun moyen de faire en sorte que votre serveur Web vous aide, la seule solution restante est readfile, il est disponible dans toutes les versions de php actuellement utilisées et fonctionne plutôt bien (mais n'est pas vraiment efficace).
Combiner des solutions
In fine, le meilleur moyen d'envoyer un fichier très rapidement si vous voulez que votre code php soit utilisable partout est d'avoir une option configurable quelque part, avec des instructions sur la façon de l'activer en fonction du serveur Web et peut-être une détection automatique dans votre installation scénario.
C'est assez similaire à ce qui est fait dans de nombreux logiciels pour
mod_rewrite
sur Apache)mcrypt
module php)mbstring
module php)la source
header("Location: " . $path);
?Le moyen le plus rapide: ne le faites pas. Regardez dans l'en -tête x-sendfile pour nginx , il y a des choses similaires pour d'autres serveurs Web également. Cela signifie que vous pouvez toujours effectuer le contrôle d'accès, etc. en php, mais déléguer l'envoi réel du fichier à un serveur Web conçu pour cela.
PS: J'éprouve des frissons en pensant à quel point son utilisation avec nginx est plus efficace que la lecture et l'envoi du fichier en php. Pensez simplement si 100 personnes téléchargent un fichier: avec php + apache, étant généreux, c'est probablement 100 * 15mb = 1,5 Go (environ, tirez-moi), de RAM juste là. Nginx transférera simplement l'envoi du fichier au noyau, puis il sera chargé directement à partir du disque dans les tampons réseau. Rapide!
PPS: Et, avec cette méthode, vous pouvez toujours faire tout le contrôle d'accès, tout ce que vous voulez dans la base de données.
la source
readfile()
Voici une solution PHP pure. J'ai adapté la fonction suivante de mon cadre personnel :
Le code est aussi efficace qu'il peut l'être, il ferme le gestionnaire de session afin que d'autres scripts PHP puissent s'exécuter simultanément pour le même utilisateur / session. Il prend également en charge le service de téléchargements par plages (ce que fait également Apache par défaut, je suppose), afin que les utilisateurs puissent suspendre / reprendre les téléchargements et bénéficier également de vitesses de téléchargement plus élevées avec des accélérateurs de téléchargement. Il vous permet également de spécifier la vitesse maximale (en Kbps) à laquelle le téléchargement (partie) doit être servi via l'
$speed
argument.la source
eio
n'est pas toujours disponible. Pourtant, +1, ne connaissait pas cette extension pecl. =)$size = sprintf('%u', filesize($path))
?Laissez Apache faire le travail à votre place.
la source
Une meilleure implémentation, avec prise en charge du cache, en-têtes http personnalisés.
la source
si vous avez la possibilité d'ajouter des extensions PECL à votre php, vous pouvez simplement utiliser les fonctions du package Fileinfo pour déterminer le type de contenu puis envoyer les en-têtes appropriés ...
la source
La
Download
fonction PHP mentionnée ici entraînait un certain retard avant que le téléchargement du fichier ne commence réellement. Je ne sais pas si cela a été causé en utilisant le cache de vernis ou quoi, mais pour moi il a aidé à enlever lesleep(1);
complètement et ensemble$speed
à1024
. Maintenant, cela fonctionne sans aucun problème car c'est rapide comme l'enfer. Vous pourriez peut-être aussi modifier cette fonction, car je l'ai vue utilisée partout sur Internet.la source
J'ai codé une fonction très simple pour servir des fichiers avec PHP et détection automatique de type MIME:
Usage
la source