38 Stimmen

Wie man große Dateien über ein PHP-Skript herunterlädt

Mit PHP versuche ich, große Dateien (bis zu 200 MB) bereitzustellen, die sich aufgrund von Berechtigungsproblemen nicht in einem über das Web zugänglichen Verzeichnis befinden. Derzeit verwende ich eine readfile() Aufruf zusammen mit einigen Kopfzeilen, um die Datei bereitzustellen, aber es scheint, dass PHP sie in den Speicher lädt, bevor es sie sendet. Ich beabsichtige, die Anwendung auf einem Shared-Hosting-Server einzusetzen, der es mir nicht erlaubt, viel Speicher zu verwenden oder meine eigenen Apache-Module wie X-Sendfile hinzuzufügen.

Aus Sicherheitsgründen kann ich meine Dateien nicht in einem über das Internet zugänglichen Verzeichnis ablegen. Kennt jemand eine weniger speicherintensive Methode, die ich auf einem Shared Hosting Server einsetzen könnte?

EDITAR:

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);

10voto

Francois Deschenes Punkte 24538

Wenn Sie fopen y fread anstelle von readfile Das sollte Ihr Problem lösen.

Es gibt eine Lösung in den PHPs readfile Dokumentation, die zeigt, wie man fread zu tun, was Sie wollen.

8voto

ursitesion Punkte 958

Um große Dateien vom Server herunterzuladen, habe ich die folgenden Einstellungen in der Datei php.ini geändert:

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

Jetzt kann ich 175 MB große Videos auf den Server hoch- und herunterladen. Denn ich habe einen dedizierten Server. Es war also einfach, diese Änderungen vorzunehmen.

Nachfolgend finden Sie das PHP-Skript zum Herunterladen der Datei. Ich habe keine Änderungen in diesem Codeschnipsel für große Dateigröße gemacht.

// 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;

3voto

Karoly Horvath Punkte 91548

Wenn Sie Wert auf Leistung legen, gibt es xsendfile, das in Apache, Nginx und Lighttpd als Modul verfügbar ist. Prüfen Sie die readfile() Kommentare der Benutzer des Docs.

Es gibt auch Module für diese Webserver, die eine URL mit einem zusätzlichen Hash-Wert akzeptieren, der das Herunterladen der Datei für eine kurze Zeitspanne ermöglicht. Dies kann auch verwendet werden, um Autorisierungsprobleme zu lösen.

1voto

Winfield Trail Punkte 5384

Sie könnten dies auch im Stil des gordischen Knotens handhaben - das heißt, Sie könnten das Problem ganz umgehen. Bewahren Sie die Dateien in einem nicht zugänglichen Verzeichnis auf, und wenn ein Download gestartet wird, können Sie einfach

$tempstring = rand();
symlink('/filestore/filename.extension', '/www/downloads' . $tempstring . '-filename.extension');
echo('Your download is available here: <a href="http://stackoverflow.com/downloads/' . $tempstring . '-filename.extension">');

und richten Sie einen Cronjob ein, der unlink() alle Download-Links, die älter als 10 Minuten sind. Es ist praktisch keine Verarbeitung Ihrer Daten erforderlich, keine Massierung von HTTP-Headern usw.

Es gibt sogar einige Bibliotheken, die genau für diesen Zweck gedacht sind.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X