Как загрузить большие файлы через PHP скрипт

Используя PHP, я пытаюсь обслуживать большие файлы (возможно, 200 МБ), которые не попадают в каталог, доступный в Интернете, из-за проблем с авторизацией. В настоящее время я использую вызов readfile() вместе с некоторыми заголовками для работы с файлом, но кажется, что PHP загружает его в память перед его отправкой. Я намерен развертывать на общем сервере хостинга, который не позволит мне использовать много памяти или добавлять мои собственные модули Apache, такие как X-Sendfile.

Я не могу позволить моим файлам находиться в каталоге, доступном в Интернете, из соображений безопасности. Кто-нибудь знает метод с меньшим объемом памяти, который я мог бы развернуть на сервере общедоступного хостинга?

EDIT:

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

Ответ 1

Если вы используете fopen и fread вместо readfile, это должно решить вашу проблему.

Там решение в документации PHP readfile, в которой показано, как использовать fread для выполнения того, что вы хотите.

Ответ 2

Чтобы загрузить большие файлы с сервера, я изменил приведенные ниже настройки в файле php.ini:

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

Теперь я могу загрузить и загрузить 175MB видео на сервере. Поскольку у меня есть выделенный сервер. Таким образом, эти изменения были легкими.

Ниже приведен файл PHP script для загрузки файла. Я не внес никаких изменений в этот фрагмент кода для большого размера файла.

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

Ответ 3

Если вы заботитесь о производительности, есть xsendfile, доступный в apache, nginx и lighttpd в качестве модуля. Проверьте readfile() комментарии пользователей doc.

Существуют также модули для этих веб-серверов, которые принимают URL-адрес с дополнительным значением хэша, который позволяет загружать файл на короткий период времени. Это также можно использовать для решения вопросов авторизации.

Ответ 4

Вы также можете справиться с этим в стиле Гордианского узла, то есть обойти проблему полностью. Храните файлы в недоступном каталоге, и при загрузке вы можете просто

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

и настройте cronjob на unlink() любые ссылки для скачивания старше 10 минут. Практически никакой обработки ваших данных не требуется, без массирования HTTP-заголовков и т.д.

Для этой цели существует даже несколько библиотек.