PHP 断点续传
在开发中遇到一个问题,我需要提供下载的文件大小由十几MB变成了2.5G以上,原有的下载方式已经不能满足需求。
原因在于PHP在处理文件下载时,读取文件将会写入缓存,默认情况下php.ini中设置的缓存阈值memory-limit为128M。所以在较大文件进行直接读取下载时,会出现下载文件大小为0的情况。对于大文件,可以做断点续传的处理。
Range 与 Content-Range()
Range位于用户请求头中,指定第一个字节的位置和最后一个字节的位置,如(Range:200-300)
Content-Range用于响应头,表示整个资源中实体表示的字节范围。
请求下载整个文件:
GET /test.rar HTTP/1.1 Connection: close Host: 116.1.219.219 Range: bytes=0-100
Range头域可以请求实体的一个或者多个子范围,Range的值为0表示第一个字节,也就是Range计算字节数是从0开始的
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
服务器将会返回HTTP/1.1 206 Partial Content,表示成功执行了范围(Range)请求。
PHP断点续传
function resumeBrokenDownloads($filePath) {
set_time_limit(0);
ini_set('memory_limit','1024M');
if(!is_file($filePath)){
die("404 File not found!");
}
$filename = basename($filePath);
header("Cache-Control: public");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".$filename);
header("Content-Transfer-Encoding: binary");
header("Accept-Ranges: bytes");
$size = filesize($filePath);
$range=0;
if (isset($_SERVER['HTTP_RANGE'])){
list($a,$range) = explode("=",$_SERVER['HTTP_RANGE']);
$size2 = $size - 1;
$new_length = $size2 - $range;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: {$new_length}");
header("Content-Range: bytes {$range}-{$size2}/{$size}");
}else{
$size2 = $size - 1;
header("Content-Range: bytes 0-{$size2}/{$size}");
header("Content-Length:".$size);
}
$fp = fopen("{$filePath}", "rb" );
fseek($fp, $range);
ob_clean();
flush();
while(!feof($fp)){
print(fread($fp,1024*8));
ob_flush();
flush();
}
fclose($fp);
exit();
}
flush()与 ob_flush()
php执行过程中,输出并非即刻执行,而是将echo或print的内容先写入buffer,当buffer满时,或者PHP执行完毕之后才会进行输出。php.ini的output_buffering参数用于配置buffer大小。
数据的流向为echo/pring -> php buffer -> tcp buffer -> browser。
位于buffer中的内容相当于处于一种等待输出状态,而flush()能将等待输出的内容输出到客户端。在开启了缓存的服务器环境中,脚本输出的内容进入了输出缓存中,尚未处于等待输出状态,此时用flush()将不会把这些内容发送到客户端,我么可以用ob_flush()将其设为等待输出状态,故在服务器中flush()需要和ob_flush()配套使用。
ob_flush() 把数据从PHP的缓冲中释放出来,即刷新PHP缓冲区
flush() 把不在缓冲中的或者说是被释放出来的数据发送到浏览器。
所以当缓冲存在的时候,我们必须ob_flush()和flush()同时使用。正确使用的顺序是:先用ob_flush(),后用flush()。
另外 ob_clean()用于清空先前缓冲区内容。