Php Readfile从大文件中读取0字节?
我正试图通过Php Readfile从大文件中读取0字节?,php,file-io,zero,abort,Php,File Io,Zero,Abort,我正试图通过readfile()发送一个大文件 但是,不会向浏览器发送任何内容,readfile()返回0(不false!) 我试图发送的文件大小为4GB,可由PHP读取。 我正在设置set\u time\u limit(0)以允许漫长的下载过程 我曾尝试在while循环装置中发送文件,并使用fread()以4K块和echo,但在下载了500-2500个MiB后,它会随机中止(无错误),并且从未成功完成下载 下面的测试代码 $f = '/var/www/tmp/largefile.dat';
readfile()
发送一个大文件
但是,不会向浏览器发送任何内容,readfile()
返回0
(不false
!)
我试图发送的文件大小为4GB,可由PHP读取。我正在设置
set\u time\u limit(0)
以允许漫长的下载过程
我曾尝试在while循环装置中发送文件,并使用fread()
以4K块和echo
,但在下载了500-2500个MiB后,它会随机中止(无错误),并且从未成功完成下载
下面的测试代码
$f = '/var/www/tmp/largefile.dat';
var_dump(file_exists($f));
var_dump(is_readable($f));
var_dump(filesize($f));
$rf = readfile($f);
var_dump($rf);
生成以下输出:
布尔(真)布尔(真)整数(4294967296)整数(0)
testfile是使用以下命令创建的:
dd if=/dev/zero of=largefile.dat bs=1M count=4096
我做错了什么?我如何修复它
编辑2014-07
升级到新的Apache2版本暂时解决了这个问题。PHP readfile在发送文件之前使用页面缓冲区存储文件。如果由于内存不足而失败,它不会抛出内存错误,只会失败 确保执行下载的页面未使用页面缓冲区() 对于大文件来说,使用fopen和fread读取文件并放置内容也更好。 另外,使用fread,您可以编写一些代码以允许继续下载 这里有一个很好的例子:
要禁用输出缓冲,您应该尝试在进程开始之前用ob_end_clean()清除并结束缓冲,或者使用ini_set禁用输出缓冲(“输出缓冲”,0)
在readfile文档中,有一个关于如何将fread用于长文件而不是readfile的示例: 在同一页面中还有一个支持部分下载的示例:
function smartReadFile($location, $filename, $mimeType='application/octet-stream') {
if(!file_exists($location)) {
header ("HTTP/1.0 404 Not Found");
return;
}
$size=filesize($location);
$time=date('r',filemtime($location));
$fm=@fopen($location,'rb');
if(!$fm) {
header ("HTTP/1.0 505 Internal server error");
return;
}
$begin=0;
$end=$size;
if(isset($_SERVER['HTTP_RANGE'])) {
if(preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)){
$begin=intval($matches[0]);
if(!empty($matches[1])){ $end=intval($matches[1]); }
}
}
if($begin>0||$end<$size){
header('HTTP/1.0 206 Partial Content');
}else{
header('HTTP/1.0 200 OK');
}
header("Content-Type: $mimeType");
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Accept-Ranges: bytes');
header('Content-Length:'.($end-$begin));
header("Content-Range: bytes $begin-$end/$size");
header("Content-Disposition: inline; filename=$filename");
header("Content-Transfer-Encoding: binary\n");
header("Last-Modified: $time");
header('Connection: close');
$cur=$begin;
fseek($fm,$begin,0);
while(!feof($fm)&&$cur<$end&&(connection_status()==0)) {
print fread($fm,min(1024*16,$end-$cur));
$cur+=1024*16;
}
}
函数smartReadFile($location,$filename,$mimeType='application/octet stream'){
如果(!file_存在($location)){
标题(“未找到HTTP/1.0 404”);
返回;
}
$size=文件大小($location);
$time=date('r',filemtime($location));
$fm=@fopen($location,'rb');
如果(!$fm){
标题(“HTTP/1.0 505内部服务器错误”);
返回;
}
$begin=0;
$end=$size;
如果(isset($_服务器['HTTP_范围]]){
如果(preg_match('/bytes=\h*(\d+)-(\d*)[\d.*]?/i',$\u服务器['HTTP_RANGE',$matches)){
$begin=intval($matches[0]);
如果(!empty($matches[1]){$end=intval($matches[1]);}
}
}
如果($begin>0 | |$END)不能回答这个问题,但是使用@deceze将如此大的文件提供给web服务器可能会更好。@deceze您的解决方案确实回答了这个问题(您应该这样制定)使用<代码> mod xsDebug文件>代码>远比下面提到的答案好,因为它使用了Apache的内部结构,并提供了比任何基于PHP的解决方案更好的稳定性和性能。只有当他或她的脚本不在Apache服务器上运行时,或者如果由于某些原因使用<代码> mod xsDebug文件> /代码>不是一个选项时,我们应该考虑给定的答案。为fread()
方法实现了可恢复下载,但它仍然会随机中止。此外,在循环之前执行ob\u end\u clean()
或readfile()
也没有效果。
function smartReadFile($location, $filename, $mimeType='application/octet-stream') {
if(!file_exists($location)) {
header ("HTTP/1.0 404 Not Found");
return;
}
$size=filesize($location);
$time=date('r',filemtime($location));
$fm=@fopen($location,'rb');
if(!$fm) {
header ("HTTP/1.0 505 Internal server error");
return;
}
$begin=0;
$end=$size;
if(isset($_SERVER['HTTP_RANGE'])) {
if(preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)){
$begin=intval($matches[0]);
if(!empty($matches[1])){ $end=intval($matches[1]); }
}
}
if($begin>0||$end<$size){
header('HTTP/1.0 206 Partial Content');
}else{
header('HTTP/1.0 200 OK');
}
header("Content-Type: $mimeType");
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Accept-Ranges: bytes');
header('Content-Length:'.($end-$begin));
header("Content-Range: bytes $begin-$end/$size");
header("Content-Disposition: inline; filename=$filename");
header("Content-Transfer-Encoding: binary\n");
header("Last-Modified: $time");
header('Connection: close');
$cur=$begin;
fseek($fm,$begin,0);
while(!feof($fm)&&$cur<$end&&(connection_status()==0)) {
print fread($fm,min(1024*16,$end-$cur));
$cur+=1024*16;
}
}