当使用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的这种行为?
你可以做的一些事情(我没有报告你需要发送的所有标题,这些标题可能与你目前在脚本中的标题相同):
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)
执行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服务器您已完成安全检查,现在他就可以传送文件了.
该脚本将一直运行,直到用户完成文件下载为止。最简单,最有效,最可靠的解决方案是重定向用户:
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)。
通过 php 下载文件效率不高,使用重定向是正确的方法。如果您不想公开文件的位置,或者文件不在公共位置,那么请查看内部重定向,这里有一篇文章对此进行了一些讨论,我可以告诉 Apache 从 PHP 进行内部重定向吗?