1.HTTP Header里的Range和Content-Range参数
Range用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式: Range:(unit=first byte pos)-[last byte pos]
Content-Range用于响应头,指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式: Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]
请求下载整个文件: GET /test.rar HTTP/1.1 Connection: close Host: 116.1.219.219 Range: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头。
一般正常回应 HTTP/1.1 200 OK Content-Length: 801 Content-Type: application/octet-stream Content-Range: bytes 0-800/801 //801:文件总大小。
2.下边是封装的一个class类方法。
类名:DownloadClass.php
<?php
namespace app\statistics\controller;
class DownloadClass
{
const BUFF_SIZE = 5120;
private $fileName;
private $filePath;
private $fileSize;
private $mimeType;
private $range;
private $isLog = false;
function __construct($fileName,$filePath,$mimeType = null,$range = null)
{
$this->fileName = $fileName;
$this->filePath = $filePath;
$this->fileSize = sprintf('%u', filesize($filePath));
$this->mimeType = ($mimeType != null) ? $mimeType : "application/octet-stream";
$this->range = trim($range);
}
private function getRange()
{
if (!empty($this->range)) {
$range = preg_replace('/[\s|,].*/', '', $this->range);
$range = explode('-', substr($range, 6));
if (count($range) < 2) {
$range[1] = $this->fileSize;
}
$range = array_combine(array('start', 'end'), $range);
if (empty($range['start'])) {
$range['start'] = 0;
}
if (!isset($range['end']) || empty($range['end'])) {
$range['end'] = $this->fileSize;
}
return $range;
}
return null;
}
public function send()
{
$fileHandle = fopen($this->filePath, 'rb');
if ($fileHandle) {
ob_end_clean();
ob_start();
ini_set('output_buffering', 'Off');
ini_set('zlib.output_compression', 'Off');
$magicQuotes = get_magic_quotes_gpc();
$lastModified = gmdate('D, d M Y H:i:s', filemtime($this->filePath)) . ' GMT';
$etag = sprintf('w/"%s:%s"', md5($lastModified), $this->fileSize);
$ranges = $this->getRange();
header(sprintf('Last-Modified: %s', $lastModified));
header(sprintf('ETag: %s', $etag));
header(sprintf('Content-Type: %s', $this->mimeType));
$disposition = 'attachment';
if (strpos($this->mimeType, 'image/') !== FALSE) {
$disposition = 'inline';
}
header(sprintf('Content-Disposition: %s; filename="%s"',$disposition,basename($this->fileName)));
if ($ranges != null) {
if ($this->isLog) {
$this->log(json_encode($ranges) . ' ' . $_SERVER['HTTP_RANGE']);
}
header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges: bytes');
header(sprintf('Content-Length: %u', $ranges['end'] - $ranges['start']));
header(sprintf('Content-Range: bytes %s-%s/%s', $ranges['start'], $ranges['end'], $this->fileSize));
fseek($fileHandle, sprintf('%u', $ranges['start']));
} else {
header("HTTP/1.1 200 OK");
header(sprintf('Content-Length: %s', $this->fileSize));
}
while (!feof($fileHandle) && !connection_aborted()) {
$lastSize = sprintf("%u",bcsub($this->fileSize, sprintf("%u", ftell($fileHandle))));
if (bccomp($lastSize, self
::BUFF_SIZE) > 0) {
$lastSize = self
::BUFF_SIZE;
}
echo fread($fileHandle, $lastSize);
ob_flush();
flush();
}
ini_set("magic_quotes_runtime",$magicQuotes);
ob_end_flush();
}
if ($fileHandle != null) {
fclose($fileHandle);
}
}
public function setIsLog($isLog = true)
{
$this->isLog = $isLog;
}
private function log($msg)
{
try {
$handle = fopen('transfer_log.txt', 'a');
fwrite($handle, sprintf('%s : %s' . PHP_EOL, date('Y-m-d H:i:s'), $msg));
fclose($handle);
} catch (\Throwable $e) {
return $e->getMessage();
}
}
}
3.使用方法
在同等级目录下可以直接new类名进行使用,不同目录需要进行use引用。
public function downLoadFile(Request
$request)
{
$fileName = 'example.mp4';
$fileName = iconv("utf-8", "gb2312//IGNORE", $fileName);
$file_path = 'G:\BaiduNetdiskDownload\example.mp4';
$change_file_info = pathinfo($file_path);
$extension = $change_file_info['extension'];
date_default_timezone_set('Asia/Shanghai');
error_reporting(E_STRICT);
$mimeType = 'audio/x-matroska';
$range = isset($_SERVER['HTTP_RANGE']) ? $_SERVER['HTTP_RANGE'] : null;
set_time_limit(0);
$transfer = new DownloadClass($fileName.'.'.$extension,$file_path,$mimeType,$range);
$transfer->send();
}
附上HTTP content-type中的所有的文件类型。
https://www.runoob.com/http/http-content-type.html