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