Por que readfile () extrai a memory do PHP?

Eu vi muitas perguntas sobre como usar o PHP com eficiência para baixar arquivos em vez de permitir solicitações diretas de HTTP (para manter arquivos seguros, rastrear downloads, etc.).

A resposta é quase sempre PHP readfile () .

  • Baixar arquivos grandes de forma confiável em PHP
  • Como forçar o download de arquivos grandes sem usar muita memory?
  • Melhor maneira de fazer logs de forma transparente?

MAS, embora seja ótimo durante o teste com arquivos enormes, quando está em um site ao vivo com centenas de usuários, os downloads começam a travar e os limites de memory do PHP estão esgotados.

Então, o que é sobre como readfile() funciona que faz com que a memory explodisse tão ruim quando o tráfego é alto? Eu pensei que era suposto o uso pesado da memory PHP, escrevendo diretamente no buffer de saída?

EDIT: (Para esclarecer, procuro um “porquê” e não “o que posso fazer”. Acho que o mod_xsendfile do Apache é a melhor maneira de contornar)

 Description int readfile ( string $filename [, bool $use_include_path = false [, resource $context ]] ) Reads a file and writes it to the output buffer*. 

O PHP tem que ler o arquivo e ele grava no buffer de saída. Assim, para o arquivo de 300Mb, não importa qual seja a implementação que você escreveu (por muitos segmentos pequenos ou por 1 pedaço grande), o PHP deve ler até 300Mb de arquivo eventualmente.

Se um usuário múltiplo tiver que baixar o arquivo, haverá um problema. (Em um servidor, os provedores de hospedagem limitarão a memory dada a cada usuário de hospedagem. Com essa memory limitada, o uso de buffer não será uma boa idéia.)

Eu acho que usar o link direto para baixar um arquivo é uma abordagem muito melhor para arquivos grandes.

Se você tiver buffer de saída do que usar ob_end_flush () logo antes da chamada para readfile ()

 header(...); ob_end_flush(); @readfile($file); 

Você pode querer desativar o buffer de saída completamente para esse local específico, usando a diretriz de configuração de saída de PHP do buffer.

Exemplo do Apache:

  ... php_admin_value output_buffering "0" ...  

“Desligado”, pois o valor parece funcionar também, enquanto ele realmente deveria lançar um erro. Pelo menos de acordo com a forma como outros tipos são convertidos em booleanos em PHP . * encolhe os ombros

Veio com essa ideia no passado (como parte da minha biblioteca) para evitar o alto uso de memory:

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

Talvez isso possa lhe dar alguma inspiração.

Como mencionado aqui: “Memória permitida … esgotada” ao usar readfile , o seguinte bloco de código no topo do arquivo php fez o truque para mim.

Isso verificará se o buffer de saída php está ativo. Se assim for, desliga-o.

 if (ob_get_level()) { ob_end_clean(); } 

Bem, é function de memory intensiva. Gostaria de canalizar usuários para um servidor estático que tenha uma regra específica estabelecida para controlar downloads em vez de usar readfile ().

Se essa não for uma opção, adicione mais RAM para satisfazer a carga ou introduza o sistema de filas que controla graciosamente o uso do servidor.