web-dev-qa-db-fra.com

Comment télécharger des fichiers volumineux via PHP

En utilisant PHP, j'essaie de servir des fichiers volumineux (jusqu'à éventuellement 200 Mo) qui ne sont pas dans un répertoire accessible sur le Web en raison de problèmes d'autorisation. Actuellement, j'utilise un appel readfile() avec quelques en-têtes pour servir le fichier, mais il semble que PHP le charge en mémoire avant de l'envoyer. J'ai l'intention de déployer sur un serveur d'hébergement partagé, qui ne me permettra pas d'utiliser beaucoup de mémoire ou d'ajouter mes propres modules Apache tels que X-Sendfile.

Je ne peux pas laisser mes fichiers dans un répertoire accessible sur le Web pour des raisons de sécurité. Quelqu'un connaît-il une méthode moins gourmande en mémoire que je pourrais déployer sur un serveur d'hébergement partagé?

ÉDITER:

if(/* My authorization here */) {
        $path = "/uploads/";
        $name = $row[0];           //This is a MySQL reference with the filename
        $fullname = $path . $name; //Create filename
        $fd = fopen($fullname, "rb");
        if ($fd) {
            $fsize = filesize($fullname);
            $path_parts = pathinfo($fullname);
            $ext = strtolower($path_parts["extension"]);
            switch ($ext) {
                case "pdf":
                header("Content-type: application/pdf");
                break;
                case "Zip":
                header("Content-type: application/Zip");
                break;
                default:
                header("Content-type: application/octet-stream");
                break;
            }
            header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\"");
            header("Content-length: $fsize");
            header("Cache-control: private"); //use this to open files directly
            while(!feof($fd)) {
                $buffer = fread($fd, 1*(1024*1024));
                echo $buffer;
                ob_flush();
                flush();    //These two flush commands seem to have helped with performance
            }
        }
        else {
            echo "Error opening file";
        }
        fclose($fd);
31
brenns10

Si vous utilisez fopen et fread au lieu de readfile, cela devrait résoudre votre problème.

Il y a ne solution dans la documentation readfile de PHP montrant comment utiliser fread pour faire ce que vous voulez.

11
Francois Deschenes

Pour télécharger des fichiers volumineux à partir du serveur, j'ai modifié les paramètres ci-dessous dans le fichier php.ini:

Upload_max_filesize  - 1500 M
Max_input_time  - 1000
Memory_limit    - 640M
Max_execution_time -  1800
Post_max_size - 2000 M

Maintenant, je peux télécharger et télécharger 175 Mo de vidéo sur le serveur. Depuis, j'ai le serveur dédié. Donc, faire ces changements était facile.

Vous trouverez ci-dessous le script PHP pour télécharger le fichier. Je n'ai apporté aucune modification à cet extrait de code pour les fichiers de grande taille.

// Begin writing headers

            ob_clean(); // Clear any previously written headers in the output buffer

            if($filetype=='application/Zip')
            {
                if(ini_get('zlib.output_compression'))
                    ini_set('zlib.output_compression', 'Off');
                $fp = @fopen($filepath, 'rb'); 
                if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
                {
                    header('Content-Type: "$content_type"');
                    header('Content-Disposition: attachment; filename="'.$filename.'"');
                    header('Expires: 0');
                    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
                    header("Content-Transfer-Encoding: binary");
                    header('Pragma: public');
                    header("Content-Length: ".filesize(trim($filepath)));
                }
                else
                {
                    header('Content-Type: "$content_type"');
                    header('Content-Disposition: attachment; filename="'.$filename.'"');
                    header("Content-Transfer-Encoding: binary");
                    header('Expires: 0');
                    header('Pragma: no-cache');
                    header("Content-Length: ".filesize(trim($filepath)));
                }

                fpassthru($fp);
                fclose($fp);
            }
            elseif($filetype=='audio'|| $filetype=='video')
            { 
                global $mosConfig_absolute_path,$my;
                ob_clean();
                header("Pragma: public");
                header('Expires: 0');
                header('Cache-Control: no-store, no-cache, must-revalidate');
                header('Cache-Control: pre-check=0, post-check=0, max-age=0');
                header("Cache-Control: public");    
                header("Content-Description: File Transfer");
                header("Content-Type: application/force-download"); 
                header("Content-Type: $content_type");
                header("Content-Length: ".filesize(trim($filepath)));
                header("Content-Disposition: attachment; filename=\"$filename\"");
                // Force the download           
                header("Content-Transfer-Encoding: binary");            
                @readfile($filepath);
            }
              else{ // for all other types of files except Zip,audio/video
                ob_clean();
                header("Pragma: public");
                header('Expires: 0');
                header('Cache-Control: no-store, no-cache, must-revalidate');
                header('Cache-Control: pre-check=0, post-check=0, max-age=0');
                header("Cache-Control: public");    
                header("Content-Description: File Transfer");
                header("Content-Type: $content_type");
                header("Content-Length: ".filesize(trim($filepath)));
                header("Content-Disposition: attachment; filename=\"$filename\"");
                // Force the download           
                header("Content-Transfer-Encoding: binary");            
                @readfile($filepath);       
            }
            exit;
5
ursitesion

Si vous vous souciez des performances, il existe xsendfile, disponible dans Apache, nginx et lighttpd en tant que module. Vérifiez les commentaires des utilisateurs du document readfile() .

Il existe également des modules pour ces serveurs Web qui acceptent une URL avec une valeur de hachage supplémentaire qui permet de télécharger le fichier pendant une courte période. Cela peut également être utilisé pour résoudre les problèmes d'autorisation.

3
Karoly Horvath

Vous pouvez également gérer cela dans le style du nœud gordien - c'est-à-dire éviter complètement le problème. Conservez les fichiers dans un répertoire non accessible, et lorsqu'un téléchargement est lancé, vous pouvez simplement

tempstring = Rand();
symlink('/filestore/filename.extension', '/www/downloads'.tempstring.'-filename.extension');
echo("Your download is available here: <a href='/downloads/'.tempstring.'-filename.extension');

et configurez un cronjob sur unlink() tout lien de téléchargement de plus de 10 minutes. Pratiquement aucun traitement de vos données n'est requis, aucun massage des en-têtes HTTP, etc.

Il existe même quelques bibliothèques dans ce but.

2
Winfield Trail