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

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

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

РЕДАКТИРОВАТЬ:

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

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

В документации по readfile PHP есть readfile показывающее, как использовать fread для выполнения того, что вы хотите.

Чтобы загрузить большие файлы с сервера, я изменил приведенные ниже настройки в файле 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 для загрузки файла. Я не внес никаких изменений в этот фрагмент кода для большого размера файла.

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

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

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

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

 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-заголовков и т. Д.

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