我正在尝试通过readfile()
发送一个大文件。
但是,没有任何内容发送到浏览器,readfile()
会返回0
(不 false
。)。
我正在尝试发送的文件是4GiB,大小可由PHP读取
我正在设置set_time_limit(0)
以允许冗长的下载过程。
我尝试在4K块和fread()
中使用echo
的while循环装置发送文件,但是在下载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);
产生以下输出:
bool(true)bool(true)int(4294967296)int(0)
使用以下命令创建测试文件:
dd if=/dev/zero of=largefile.dat bs=1M count=4096
我做错了什么以及如何解决?
编辑2014-07
升级到新的Apache2版本现在已经解决了这个问题。
答案 0 :(得分:2)
PHP readfile在发送文件之前使用页面缓冲区来存储文件。如果由于内存不足而失败,则不会引发内存错误,只会失败。
确保执行下载的页面未使用页面缓冲区(http://us1.php.net/manual/es/ref.outcontrol.php)
对于大文件来说,使用fopen fread读取文件并放入内容也是更好的选择。 还有fread,您可以制作一些代码以允许恢复下载。
这里有一个很好的例子:Resumable downloads when using PHP to send the file?
要禁用输出缓冲,您应该尝试使用ob_end_clean()在进程启动之前清理并结束缓冲区,或者使用ini_set('output_buffering',0)禁用输出缓冲;
在readfile文档中有一个关于如何将fread用于长文件而不是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;
}
此外,还有一个在同一页面中提供部分下载支持的示例:
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;
}
}