Я видел много вопросов о том, как эффективно использовать PHP для загрузки файлов, а не для прямого HTTP-запроса (чтобы защитить файлы, отслеживать загрузки и т. Д.).
Ответ почти всегда на PHP readfile () .
НО, хотя он отлично работает во время тестирования с огромными файлами, когда он находится на живом сайте с сотнями пользователей, загрузки начинают зависать, а пределы памяти PHP исчерпаны.
Итак, что же происходит с readfile()
что приводит к тому, что память настолько сильно readfile()
когда трафик высок? Я думал, что он должен обойти тяжелое использование памяти PHP, написав непосредственно в выходной буфер?
EDIT: (Чтобы уточнить, я ищу «почему», а не «что я могу сделать». Я думаю, что mod_xsendfile Apache – лучший способ обойти)
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 МБ файла.
Если несколько пользователей должны загрузить файл, возникнет проблема. (На одном сервере хостинг-провайдеры ограничивают память, предоставляемую каждому пользователю хостинга. При такой ограниченной памяти использование буфера не будет хорошей идеей.)
Я думаю, что использование прямой ссылки для загрузки файла – гораздо лучший подход для больших файлов.
Если у вас есть буферизация вывода, чем использование ob_end_flush () прямо перед вызовом readfile ()
header(...); ob_end_flush(); @readfile($file);
Возможно, вы захотите отключить буферизацию вывода для этого конкретного места, используя директиву конфигурации output_buffering PHP .
Пример Apache:
<Directory "/your/downloadable/files"> ... php_admin_value output_buffering "0" ... </Directory>
«Off», поскольку значение, похоже, тоже работает, в то время как оно действительно должно вызывать ошибку. По крайней мере, согласно тому, как другие типы преобразуются в булевы в PHP . * пожимает плечами *
В прошлом эта идея использовалась как часть моей библиотеки, чтобы избежать использования высокой памяти:
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 ); }
Возможно, это может дать вам некоторое вдохновение.
Как уже упоминалось: «Разрешенная память .. исчерпана» при использовании readfile , следующий код кода в верхней части файла php сделал для меня трюк.
Это проверит, активна ли буферизация php-вывода. Если это так, он отключается.
if (ob_get_level()) { ob_end_clean(); }
Ну, это интенсивная память. Я бы подключил пользователей к статическому серверу, на котором установлено определенное правило для управления загрузками вместо использования readfile ().
Если это не вариант, добавьте больше ОЗУ, чтобы удовлетворить нагрузку или ввести систему очередей, которая изящно контролирует использование сервера.