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