Обслуживание больших файлов с помощью PHP - PullRequest
12 голосов
/ 11 января 2009

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

Единственный способ, которым я мог придумать, чтобы обслужить этот файл, - это загрузить его в память (fopen, fread, и т. файл.

Проблема в том, что я должен загрузить эти ~ 700 МБ файлы в память одновременно и сохранить все это до завершения загрузки. Было бы неплохо, если бы я мог транслировать нужные мне части по мере их загрузки.

Есть идеи?

Ответы [ 8 ]

25 голосов
/ 11 января 2009

Вам не нужно читать все целиком - просто введите цикл, читая его, скажем, на 32-килобайтных блоках и отправляя его в качестве вывода. Еще лучше, используйте fpassthru , который делает то же самое для вас ....

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

еще меньше строк , если вы используете readfile , который не требует вызова 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;

Если вы хотите получить еще более привлекательную информацию, вы можете поддерживать заголовок Content-Range , который позволяет клиентам запрашивать определенный диапазон байтов вашего файла. Это особенно полезно для передачи файлов PDF в Adobe Acrobat, который просто запрашивает фрагменты файла, необходимые для рендеринга текущей страницы. Это немного запутанно, но посмотрите пример .

10 голосов
/ 11 января 2009

Лучший способ отправить большие файлы с помощью php - заголовок X-Sendfile. Это позволяет веб-серверу обслуживать файлы намного быстрее с помощью механизмов нулевого копирования, таких как sendfile(2). Поддерживается lighttpd и apache с плагином .

Пример:

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

Сервер читает заголовок X-Sendfile и отправляет файл.

6 голосов
/ 11 января 2009

Хотя fpassthru() был моим первым выбором в прошлом, руководство по PHP на самом деле рекомендует * использовать readfile() вместо этого, если вы просто выгружаете файл как есть клиенту. *

* "Если вы просто хотите сбросить содержимое файла в выходной буфер, без предварительного изменения его или поиска определенного смещения, вы можете использовать readfile (), который спасает вас fopen ( ) вызов." - PHP инструкция

2 голосов
/ 11 января 2009

Если ваши файлы недоступны веб-серверу, потому что путь не находится в вашем каталоге веб-обслуживания (htdocs), вы можете сделать символическую ссылку (символическую ссылку) на эту папку в вашем каталоге веб-обслуживания, чтобы избежать пропуска всего трафика PHP.

Вы можете сделать что-то вроде этого

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

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

1 голос
/ 23 августа 2011

Странно, ни fpassthru (), ни readfile () не сделали это для меня, всегда была ошибка памяти. Я прибег к использованию passthru () без '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;

эта execs 'cat' команда Unix и отправка ее вывода в браузер.

комментарий для slim: причина, по которой вы не помещаете символическую ссылку куда-то, в том, что веб-пространство - это SECURITY.

1 голос
/ 11 января 2009

Посмотрите на fpassthru () . В более поздних версиях PHP это должно обслуживать файлы, не сохраняя их в памяти, так как этот комментарий заявляет.

0 голосов
/ 11 января 2009

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

0 голосов
/ 11 января 2009

Одним из преимуществ fpassthru () является то, что эта функция может работать не только с файлами, но и с любым допустимым дескриптором. Сокет например.

И readfile () должен быть немного быстрее, по возможности использовать механизм кэширования ОС (например, file_get_contents ()).

Еще один совет. fpassthru () держит дескриптор открытым, пока клиент не получит контент (что может потребовать довольно продолжительного времени при медленном соединении), и поэтому вы должны использовать некоторый механизм блокировки, если возможна параллельная запись в этот файл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...