PHP readfile()和大文件

Mik*_* C. 11 php apache

当使用readfile() - 在Apache上使用PHP时 - 文件会立即读入Apache的输出缓冲区并完成PHP脚本执行,或者PHP脚本执行是否等到客户端完成下载文件(或服务器超时,以先发生者为准)?

较长的背景故事:

我有一个网站,里面有很多大型的mp3文件(当地教会的布道).并非允许下载音频存档中的所有文件,因此/sermon/{filename}.mp3路径将被重写为真正执行/sermon.php?filename={filename},如果允许下载文件,则内容类型设置为"audio/mpeg",文件使用readfile()流出.我一直在接受投诉(几乎全部来自通过3G下载流媒体的iPhone用户)文件没有完全下载,或者他们在大约10或15分钟后切断了.当我从带有readfile()的文件流式传输切换到简单地重定向到文件 - header("Location:$ file_url"); - 所有投诉都消失了(我甚至与一些用户进行了核实,这些用户可以根据需要可靠地重现问题).

这让我怀疑当使用readfile()时,PHP脚本引擎正在使用,直到文件被完全下载,但我找不到任何确认或否认这个理论的引用.我承认我在ASP.NET世界中更像是家,而dotNet等效的readfile()会立即将整个文件推送到IIS输出缓冲区,因此ASP.NET执行管道可以独立于文件的传递而完成到最终客户端...是否有相当于PHP + Apache的这种行为?

Fab*_*zio 8

你可以做的一些事情(我没有报告你需要发送的所有标题,这些标题可能与你目前在脚本中的标题相同):

set_time_limit(0);  //as already mention
readfile($filename);
exit(0);
Run Code Online (Sandbox Code Playgroud)

要么

passthru('/bin/cat '.$filename);
exit(0);
Run Code Online (Sandbox Code Playgroud)

要么

//When you enable mod_xsendfile in Apache
header("X-Sendfile: $filename");
Run Code Online (Sandbox Code Playgroud)

要么

//mainly to use for remove files
$handle = fopen($filename, "rb");
echo stream_get_contents($handle);
fclose($handle);
Run Code Online (Sandbox Code Playgroud)

要么

$handle = fopen($filename, "rb");
while (!feof($handle)){
    //I would suggest to do some checking
    //to see if the user is still downloading or if they closed the connection
    echo fread($handle, 8192);
}
fclose($handle);
Run Code Online (Sandbox Code Playgroud)


reg*_*ero 7

执行readfile()时,您仍可能激活PHP输出缓冲.用以下方法检查:

if (ob_get_level()) ob_end_clean();
Run Code Online (Sandbox Code Playgroud)

要么

while (ob_get_level()) ob_end_clean();
Run Code Online (Sandbox Code Playgroud)

这种方式只剩下输出缓冲区应该是apache的输出缓冲区,请参阅SendBufferSize以进行apache调整.

编辑

您还可以查看mod_xsendfile(关于此类用法的SO帖子,PHP + apache + x-sendfile),这样您只需告诉Web服务器您已完成安全检查,现在他就可以传送文件了.


Luc*_*Luc 5

该脚本将一直运行,直到用户完成文件下载为止。最简单,最有效,最可靠的解决方案是重定向用户:

header("Location: /real/path/to/file");
exit;
Run Code Online (Sandbox Code Playgroud)

但这可能会显示文件的位置。最好使用.htaccess文件对可能无法所有人都下载的文件进行密码保护,但是也许您使用数据库确定访问权限,这是不可行的。

另一个可能的解决方案是将PHP的最大执行时间设置为0,这将禁用该限制:

set_time_limit(0);
Run Code Online (Sandbox Code Playgroud)

不过,您的房东可能不允许这样做。同样,PHP首先将文件读入内存,然后通过Apache的输出缓冲区,最后将其发送到网络。使用户直接下载文件效率更高,并且没有PHP的限制,例如最长执行时间。

编辑:您从iPhone用户那里得到很多抱怨的原因可能是他们的连接速度较慢(例如3G)。


hac*_*ack 3

通过 php 下载文件效率不高,使用重定向是正确的方法。如果您不想公开文件的位置,或者文件不在公共位置,那么请查看内部重定向,这里有一篇文章对此进行了一些讨论,我可以告诉 Apache 从 PHP 进行内部重定向吗?