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

时间:2011-07-08 17:12:11

标签: php memory readfile

我见过很多关于如何有效使用PHP下载文件的问题,而不是允许直接的HTTP请求(保证文件安全,跟踪下载等)。

答案几乎总是PHP readfile()

但是,虽然它在使用大文件进行测试时效果很好,但是当它在拥有数百个用户的实时网站上时,下载开始挂起并且PHP内存限制已经用尽。

那么readfile()如何工作导致内存在流量高时爆炸如此糟糕呢?我以为它应该通过直接写入输出缓冲区绕过大量使用PHP内存?

编辑:(澄清一下,我正在寻找“为什么”,而不是“我能做什么”。我认为Apache的mod_xsendfile是绕过的最佳方式)

6 个答案:

答案 0 :(得分:5)

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

PHP必须读取文件并将其写入输出缓冲区。 因此,对于300Mb文件,无论您编写的实现(由许多小段或1个大块),PHP最终都必须读取300Mb的文件。

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

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

答案 1 :(得分:2)

如果你有输出缓冲,那么在调用readfile()之前使用ob_end_flush()

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

答案 2 :(得分:1)

您可能希望使用PHP's output_buffering configuration指令完全关闭该特定位置的输出缓冲。

Apache示例:

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

“关闭”因为值似乎也有效,但它确实会引发错误。至少根据how other types are converted to booleans in PHP *耸肩 *

答案 3 :(得分:1)

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

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

也许这会给你一些启发。

答案 4 :(得分:1)

如前所述:"Allowed memory .. exhausted" when using readfile,php文件顶部的以下代码块为我做了诀窍。

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

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

答案 5 :(得分:0)

嗯,这是内存密集型功能。我会将用户管道到一个静态服务器,该服务器具有适当的规则集来控制下载而不是使用readfile()。

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