Обслуживание больших файлов с помощью PHP

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

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

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

Любые идеи?

Они недоступны из-за аутентификации?

mark 11.01.2009 15:12
stackoverflow.com/questions/3697748/…
Keyne Viana 18.11.2011 20:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
13
2
13 477
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Ответ принят как подходящий

Вам не нужно читать все целиком - просто войдите в цикл, читая его, скажем, фрагментами по 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;

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

Спасибо! Я не понимал, что могу обойтись Content-Range с Adobe.

David 11.01.2009 15:49

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

cnvzmxcvmcx 07.06.2014 11:38

И readfile, и fpassthru для меня не работают с ошибками, связанными с памятью: Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 1883504640 bytes) ...

Ionică Bizău 02.08.2019 07:30

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

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

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

Лучший способ отправлять большие файлы с помощью 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 и отправляет файл.

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

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

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

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

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

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

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

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

Странно, ни 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;

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

комментарий для slim: причина, по которой вы просто не помещаете где-то символическую ссылку, - это веб-пространство - БЕЗОПАСНОСТЬ.

Если вы хотите сделать это правильно, только PHP не сможет этого сделать. Вы могли бы обслуживать файл с помощью Nginx X-Accel-Redirect(Рекомендуемые) или Apache X-Sendfile, которые созданы именно для этой цели.

Я включу в этот ответ текст, найденный на этом статья.

Почему бы не обслуживать файлы с помощью PHP:

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

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

Добавьте это в свой серверный блок nginx:

location /protected_files/ {
    internal;
    alias /var/www/my_folder_with_protected_files/;
}

В вашем проекте потребуется пакет HTTP Foundation:

composer require symfony/http-foundation

Подавайте файлы на PHP с помощью Nginx:

use Symfony\Component\HttpFoundation\BinaryFileResponse;

$real_path = '/var/www/my_folder_with_protected_files/foo.pdf';
$x_accel_redirect_path = '/protected_files/foo.pdf';

BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse( $real_path );
$response->headers->set( 'X-Accel-Redirect', $accel_file );
$response->sendHeaders();
exit;

Это должно быть основным, что вам нужно для начала работы.

Вот более полный пример работы со встроенным PDF-файлом:

use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

$real_path = '/var/www/my_folder_with_protected_files/foo.pdf';
$x_accel_redirect_path = '/protected_files/foo.pdf';

$file = new File( $file_path );

BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse( $file_path );
$response->setImmutable( true );
$response->setPublic();
$response->setAutoEtag();
$response->setAutoLastModified();
$response->headers->set( 'Content-Type', 'application/pdf' );
$response->headers->set( 'Content-Length', $file->getSize() );
$response->headers->set( 'X-Sendfile-Type', 'X-Accel-Redirect' );
$response->headers->set( 'X-Accel-Redirect', $accel_file );
$response->headers->set( 'X-Accel-Expires', 60 * 60 * 24 * 90 ); // 90 days
$response->headers->set( 'X-Accel-Limit-Rate', 10485760 ); // 10mb/s
$response->headers->set( 'X-Accel-Buffering', 'yes' );
$response->setContentDisposition( ResponseHeaderBag::DISPOSITION_INLINE, basename( $file_path ) ); // view in browser. Change to DISPOSITION_ATTACHMENT to download
$response->sendHeaders();
exit;

Другие вопросы по теме