读取文件从大文件读取0字节? [英] Readfile reads 0 bytes from large file?

查看:339
本文介绍了读取文件从大文件读取0字节?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图通过 readfile()发送一个大文件。然而,没有任何东西被发送到 readfile()返回 0 not 假设!)。

我想发送的文件是4GiB大小,PHP可读。

我设置 set_time_limit(0)允许一个漫长的下载过程。

我试图发送文件一个while循环的巧妙处理是在4K块和 echo 中使用 fread(),但是会在随后(没有错误)下载测试代码

下载测试代码  $ 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);

产生以下输出:


bool(true)bool(true)int(4294967296)int(0)

以下命令:

  dd if = / dev / zero of = largefile.dat bs = 1M count = 4096 

我做错了什么,如何解决?

< hr>

编辑2014-07

升级到新版本的Apache2已经解决了这个问题。 $ b

解决方案

PHP读取文件在发送之前使用页面缓冲区来存储文件。如果由于内存不足而失败,则不会抛出内存错误,只会失败。确保执行下载的页面没有使用页面缓冲区( outcontrol.phprel =nofollow noreferrer> http://us1.php.net/manual/es/ref.outcontrol.php

对于大文件来说,更好的办法是使用fadn来读取文件并放入内容。
也可以用fread代码来允许下载继续。



这里有一个很好的例子:



要禁用输出缓冲,您应该尝试在使用ob_end_clean()启动进程之前清理并结束缓冲区,或者使用ini_set('output_buffering',0)禁用输出缓冲。 ;




在readfile文档中有一个关于如何将fread用于长文件而不是readfile的示例:



http:// ca2 .php.net / manual / en / function.readfile.php#48683
$ b

 函数readfile_chunked($ filename, $ retbytes = true){
$ chunksize = 1 *(1024 * 1024); //每个块有多少个字节
$ buffer ='';
$ cnt = 0;
$ handle = fopen($ filename,'rb');
if($ handle === false){
return false;

while(!feof($ handle)){
$ buffer = fread($ handle,$ chunksize);
echo $ buffer;
ob_flush();
flush();
if($ retbytes){
$ cnt + = strlen($ buffer);
}
}
$ status = fclose($ handle);
if($ retbytes&& $ status){
return $ cnt; //返回数字像readfile()这样传递的字节可以。
}
返回$ status;






在同一页面中还有一个支持部分下载的例子:
$ b $ pre $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ 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;
$ b $ if(isset($ _ SERVER ['HTTP_RANGE'])){
if(preg_match('/ bytes = \h *(\ d +) - (\d *) $ _SERVER ['HTTP_RANGE'],$ matches)){
$ begin = intval($ matches [0]);
if(!empty($ matches [1])){$ end = intval($ matches [1]);如果($ 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);
头(Content-Transfer-Encoding:binary \\\
);
header(Last-Modified:$ time);
header('Connection:close');

$ cur = $ begin;
fseek($ fm,$ begin,0); ($ fe $($ fm)&& $ cur< $ end&&(connection_status()== 0)){
print fread($ fm,min( 1024 * 16,$最终$ CUR));
$ cur + = 1024 * 16;
}
}


I'm trying to send a large file through readfile().

However, nothing is sent to the browser and readfile() returns 0 (not false!).

The file I'm trying to send is 4GiB of size and readable by PHP.
I am setting set_time_limit(0) to allow a lengthy download process.

I have tried to send the file in a while-loop contraption with fread() in 4K chunks and echo, but that aborts randomly (without error) after 500 - 2500 MiB downloaded and never manages to complete the download.

The following test code

$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);

produces the following output:

bool(true) bool(true) int(4294967296) int(0)

The testfile was created using the following command:

dd if=/dev/zero of=largefile.dat bs=1M count=4096

What am I doing wrong and how do I fix it?


Edit 2014-07
Upgrading to a new Apache2 version has solved the problem for now.

解决方案

PHP readfile uses the page buffer to store the file before send it. If it fails due to insufficient memory it does not throw a memory error only fail.

Ensure that the page that performing the download is not using page buffer( http://us1.php.net/manual/es/ref.outcontrol.php)

Also is better for big files to use fopen an fread to read the file and put the content. Also with fread you can make some code to allow the download to be resumed.

Here there's a good example: Resumable downloads when using PHP to send the file?


To disable ouput buffering you should try to clean and end buffer before process start with ob_end_clean() or disable the output buffering using ini_set('output_buffering', 0);


In the readfile documentation there's an example about how to use fread for long files instead of readfile:

http://ca2.php.net/manual/en/function.readfile.php#48683

function readfile_chunked($filename,$retbytes=true) {
   $chunksize = 1*(1024*1024); // how many bytes per chunk
   $buffer = '';
   $cnt =0;
   $handle = fopen($filename, 'rb');
   if ($handle === false) {
       return false;
   }
   while (!feof($handle)) {
       $buffer = fread($handle, $chunksize);
       echo $buffer;
       ob_flush();
       flush();
       if ($retbytes) {
           $cnt += strlen($buffer);
       }
   }
       $status = fclose($handle);
   if ($retbytes && $status) {
       return $cnt; // return num. bytes delivered like readfile() does.
   }
   return $status;

} 

Also there's an example with partial download support in the same page:

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

这篇关于读取文件从大文件读取0字节?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆