Почему readfile () исчерпывает память PHP? - PullRequest
22 голосов
/ 08 июля 2011

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

Ответ почти всегда PHP readfile () .

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

Так что же такого в том, как работает readfile(), что приводит к тому, что память так сильно взрывается при высоком трафике?Я думал, что это должно обойти интенсивное использование памяти PHP, записывая непосредственно в выходной буфер?

РЕДАКТИРОВАТЬ: (Чтобы уточнить, я ищу «почему», а не «что я могу сделать». Ядумаю, что Apache mod_xsendfile - лучший способ обойти)

Ответы [ 6 ]

5 голосов
/ 08 июля 2011
Description
int readfile ( string $filename [, bool $use_include_path = false [, resource $context ]] )
Reads a file and writes it to the output buffer*.

PHP должен прочитать файл и записать в выходной буфер.Таким образом, для файла размером 300 МБ, независимо от того, какую реализацию вы написали (многими небольшими сегментами или одним большим фрагментом), PHP должен в конечном итоге прочитать 300 МБ файла.будет проблема.(На одном сервере хостинг-провайдеры ограничат объем памяти, выделяемой каждому пользователю хостинга. При такой ограниченной памяти использование буфера не будет хорошей идеей.)

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

2 голосов
/ 16 ноября 2012

Если у вас есть выходная буферизация, используйте ob_end_flush () прямо перед вызовом readfile ()

header(...);
ob_end_flush();
@readfile($file);
1 голос
/ 17 августа 2016

Как упомянуто здесь: «Разрешенная память .. исчерпана» при использовании readfile , следующий блок кода в верхней части php-файла помог мне.

Это будетпроверяет, активна ли выходная буферизация php.Если это так, он выключает его.

if (ob_get_level()) {
    ob_end_clean();
}
1 голос
/ 25 июля 2013

В прошлом (как часть моей библиотеки) пришла в голову эта идея, чтобы избежать чрезмерного использования памяти:

function suTunnelStream( $sUrl, $sMimeType, $sCharType = null )
{
  $f = @fopen( $sUrl, 'rb' );
  if( $f === false )
   { return false; }

  $b = false;
  $u = true;

  while( $u !== false && !feof($f ))
  { 
    $u = @fread( $f, 1024 );
    if( $u !== false )
    {  
      if( !$b )
       { $b = true;
         suClearOutputBuffers();
         suCachedHeader( 0, $sMimeType, $sCharType, null, !suIsValidString($sCharType)?('content-disposition: attachment; filename="'.suUniqueId($sUrl).'"'):null );
       } 
      echo $u; 
    }
 } 

  @fclose( $f );
  return ( $b && $u !== false );
}

Возможно, это может вдохновить вас.

1 голос
/ 05 октября 2012

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

Пример Apache:

<Directory "/your/downloadable/files">
     ...
     php_admin_value output_buffering "0"
     ...
</Directory>

«Выкл.», Поскольку значение, похоже, тоже работает, хотя в действительности оно должно выдавать ошибку. По крайней мере, согласно , как другие типы преобразуются в логические значения в PHP . * пожимает *

0 голосов
/ 08 июля 2011

Ну, это интенсивная память.Я бы направил пользователей на статический сервер, на котором установлены определенные правила для управления загрузками вместо использования readfile ().

Если это не вариант, добавьте больше ОЗУ, чтобы удовлетворить нагрузку, или добавьте систему очередей, которая изящно контролируетиспользование сервера.

...