为什么readfile()会耗尽PHP内存?

为什么readfile()会耗尽PHP内存?,php,memory,readfile,Php,Memory,Readfile,我已经看到了许多关于如何高效地使用PHP下载文件而不是允许直接HTTP请求(保持文件安全、跟踪下载等)的问题 答案几乎总是如此 但是,尽管它在测试大文件时工作得很好,但当它在一个有数百名用户的实时站点上时,下载开始挂起,PHP内存限制耗尽 那么,当通信量很大时,readfile()?我认为它应该通过直接写入输出缓冲区来绕过PHP内存的大量使用 编辑:(为了澄清,我在寻找一个“为什么”,而不是“我能做什么”。我认为Apache是最好的规避方法) PHP必须读取文件并写入输出缓冲区。 因此

我已经看到了许多关于如何高效地使用PHP下载文件而不是允许直接HTTP请求(保持文件安全、跟踪下载等)的问题

答案几乎总是如此

但是,尽管它在测试大文件时工作得很好,但当它在一个有数百名用户的实时站点上时,下载开始挂起,PHP内存限制耗尽

那么,当通信量很大时,
readfile()?我认为它应该通过直接写入输出缓冲区来绕过PHP内存的大量使用

编辑:(为了澄清,我在寻找一个“为什么”,而不是“我能做什么”。我认为Apache是最好的规避方法)

PHP必须读取文件并写入输出缓冲区。 因此,对于300Mb的文件,无论您编写了什么实现(通过许多小片段,或通过一大块),PHP最终都必须读取300Mb的文件

如果多个用户必须下载该文件,则会出现问题。 (在一台服务器中,托管提供商将限制提供给每个托管用户的内存。由于内存有限,使用缓冲区不是一个好主意。)


我认为使用直接链接下载文件对于大文件来说是一种更好的方法。

嗯,这是一种内存密集型功能。我将通过管道将用户连接到一个静态服务器,该服务器设置了控制下载的特定规则,而不是使用readfile()


如果这不是一个选项,请添加更多RAM以满足负载要求,或者引入队列系统以优雅地控制服务器的使用。

您可能希望使用指令完全关闭该特定位置的输出缓冲

Apache示例:

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

...
php_管理_值输出_缓冲“0”
...

“Off”作为值似乎也起作用,但它确实应该抛出一个错误。至少据我所知*耸肩*

如果打开了输出缓冲,则在调用readfile()之前使用ob_end_flush()


在过去(作为我的库的一部分)提出了这个想法,以避免高内存使用率:

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 );
}
也许这会给你一些启发。

正如这里提到的:,php文件顶部的以下代码块为我实现了这个技巧

这将检查php输出缓冲是否处于活动状态。如果是这样,它会关闭它

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

您是否打开了PHP输出缓冲(通过
ob_start()
)?如果是这样,PHP将捕获文件内容并导致内存问题;冲洗();readfile($file);那么,如果PHP为UserA“写入输出缓冲区”——它使用的是UserB和UserC无法使用的内存?最终导致内存过度使用?是的。由于PHP不知道其他用户何时下载同一个文件,PHP只需将文件读取到缓冲区,并尽快将其写入请求程序。。。如果100个用户请求相同的10MB文件,内存使用量可能是1000MB。。。所以readfile()并不能完全做到这一点,在高流量情况下应该避免。谢谢。您的回答表明即使在缓冲小段时也存在此问题。在这种情况下,100个用户请求10MB的文件不会导致1000MB的内存使用,而是100倍的缓冲区大小。对吗?是的,没错,我刚才说的“总”内存使用量到最后将达到1000MB。在某个时刻,它将是
100*buffer\u size
在对2GB文件执行readfile之前,我尝试添加这个,但它仍然超出了内存限制。如上所述,输出缓冲不是问题。因为问题是“为什么”,我想你应该提供一个解释,而不是修复。毕竟,你基本上是在复制一个已有的答案。啊,是的。我接着补充了一个简短的解释。
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 );
}
if (ob_get_level()) {
    ob_end_clean();
}