Servir de gros fichiers avec PHP

J’essaie donc de servir des fichiers volumineux via un script PHP, ils ne sont pas dans un répertoire accessible par le Web, c’est donc la meilleure façon de pouvoir y accéder.

La seule façon de penser à servir ce fichier est de le charger en mémoire (fopen, fread, ect.), De définir les données d’en-tête sur le type MIME approprié, puis de renvoyer tout le contenu du fichier.

Le problème est que je dois charger ces fichiers ~ 700MB en mémoire en une seule fois, et conserver tout cela jusqu’à ce que le téléchargement soit terminé. Ce serait bien si je pouvais diffuser les parties dont j’avais besoin au fur et à mesure de leur téléchargement.

Des idées?

Vous n’avez pas besoin de lire le tout – il suffit d’entrer une boucle en le lisant, par exemple, en morceaux de 32 Ko et en l’envoyant en sortie. Mieux encore, utilisez fpassthru qui fait la même chose pour vous ….

$name = 'mybigfile.zip'; $fp = fopen($name, 'rb'); // send the right headers header("Content-Type: application/zip"); header("Content-Length: " . filesize($name)); // dump the file and stop the script fpassthru($fp); exit; 

encore moins de lignes si vous utilisez readfile , qui n’a pas besoin de l’appel fopen …

 $name = 'mybigfile.zip'; // send the right headers header("Content-Type: application/zip"); header("Content-Length: " . filesize($name)); // dump the file and stop the script readfile($name); exit; 

Si vous voulez être encore plus mignon, vous pouvez prendre en charge l’en – tête Content-Range qui permet aux clients de demander une plage d’octets particulière de votre fichier. Ceci est particulièrement utile pour servir des fichiers PDF à Adobe Acrobat, qui demande simplement les morceaux du fichier dont il a besoin pour afficher la page en cours. C’est un peu compliqué, mais voyez ceci pour un exemple .

Le meilleur moyen d’envoyer de gros fichiers avec php est l’en X-Sendfile tête X-Sendfile . Il permet au serveur Web de servir des fichiers beaucoup plus rapidement grâce à des mécanismes sans copie comme sendfile(2) . Il est supporté par lighttpd et apache avec un plugin .

Exemple:

 $file = "/absolute/path/to/file"; // can be protected by .htaccess header('X-Sendfile: '.$file); header('Content-type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.basename($file).'"'); // other headers ... exit; 

Le serveur lit l’en X-Sendfile tête X-Sendfile et envoie le fichier.

Bien que fpassthru() ait été mon premier choix par le passé, le manuel PHP recommande en fait * d’utiliser readfile() si vous ne faites que transférer le fichier tel readfile() sur le client.

* “Si vous voulez simplement sauvegarder le contenu d’un fichier dans le tampon de sortie, sans le modifier ou chercher un décalage particulier, vous pouvez utiliser le fichier readfile (), qui vous évite l’appel fopen ().” – Manuel PHP

Si vos fichiers ne sont pas accessibles par le serveur Web car le chemin n’est pas dans votre répertoire de serveurs Web (htdocs), vous pouvez créer un lien symbolique (lien symbolique) vers ce dossier dans votre répertoire de serveurs Web.

Vous pouvez faire quelque chose comme ça

 ln -s /home/files/big_files_folder /home/www/htdocs 

L’utilisation de PHP pour servir des fichiers statiques est beaucoup plus lente, si le trafic est élevé, la consommation de mémoire sera très importante et ne traitera pas un grand nombre de requêtes.

Regardez fpassthru () . Dans les versions plus récentes de PHP, cela devrait servir les fichiers sans les garder en mémoire, comme le dit ce commentaire .

Étrange, ni fpassthru () ni readfile () ne l’ont fait pour moi, toujours eu une erreur de mémoire. J’ai eu recours à passthru () sans le ‘f’:

 $name = 'mybigfile.zip'; // send the right headers header("Content-Type: application/zip"); header("Content-Length: " . filesize($name)); // dump the file and stop the script passthru('/bin/cat '.$filename); exit; 

cette commande Unix de chat execs et envoie sa sortie au navigateur.

commentaire pour slim: la raison pour laquelle vous ne mettez pas un lien symbolique quelque part, c’est que webspace est SECURITY.

Un des avantages de fpassthru () est que cette fonction peut fonctionner non seulement avec des fichiers, mais aussi avec un handle valide. Socket par exemple.

Et readfile () doit être un peu plus rapide, car il utilise, si possible, le mécanisme de mise en cache du système d’exploitation (comme par exemple file_get_contents ()).

Un autre pourboire. fpassthru () maintient le handle ouvert jusqu’à ce que le client obtienne du contenu (ce qui peut nécessiter beaucoup de temps lors d’une connexion lente), et vous devez donc utiliser un mécanisme de locking si des écritures parallèles sont possibles dans ce fichier.

Les réponses Python sont toutes bonnes. Mais y a-t-il une raison pour laquelle vous ne pouvez pas créer un répertoire accessible par le Web contenant des liens symboliques vers les fichiers réels? Cela peut nécessiter une configuration supplémentaire du serveur, mais cela devrait fonctionner.