<?phpnamespace extend;
use Exception;
class DownLoad
{
private $siteUrl = '';
private $header = [];
private $burstBytes = 1048576;
public function setUrl($url)
{
$this->siteUrl = $url;
return $this;
}
public function setBurst($byte)
{
$this->burstBytes = $byte;
return $this;
}
public function setHeader($header)
{
$this->header = $header;
return $this;
}
private function getSiteFiLeInfo()
{
if (!$this->siteUrl) {
throw new Exception('请先设置远程文件url!');
}
$responseHeader = get_headers($this->siteUrl, 1);
if (!$responseHeader) {
throw new Exception('获取远程文件信息失败!');
}
if (!empty($responseHeader['Location'])) {
$this->siteUrl = $responseHeader['Location'];
return $this->getSiteFiLeInfo();
}
return $responseHeader;
}
public function saveFile($fileName)
{
$siteFileInfo = $this->getSiteFiLeInfo();
$siteFileLength = $siteFileInfo['Content-Length'] ?? 0;
if (file_exists($fileName)) {
$fd = fopen($fileName, 'ab');
} else {
$fd = fopen($fileName, 'wb');
}
if (!$fd) {
throw new Exception('创建或打开本地文件失败!');
}
if (!flock($fd, LOCK_EX | LOCK_NB)) {
throw new Exception('已有相关进程操作执行下载本文件!');
}
$fileSize = filesize($fileName);
if ($fileSize && $fileSize >= $siteFileLength) {
}
$sByte = $fileSize;
$eByte = $sByte + $this->burstBytes;
while (true) {
if ($fileSize >= $siteFileLength) {
fclose($fd);
break;
}
$xRange = "{$sByte}-{$eByte}";
$result = $this->curl($xRange);
$code = $result['code'] ?? 0;
if (!$code) {
throw new Exception('Http请求异常!');
}
if ($code != 206) {
throw new Exception('Http状态码异常,可能不支持断点的资源或已完成下载!');
}
$streamLength = $result['length'] ?? 0;
$streamContent = $result['stream'] ?? '';
if ($streamLength > 0) {
$saveRes = fwrite($fd, $streamContent);
if (!$saveRes) {
throw new Exception('写入流到文件失败!');
}
if ($saveRes != $streamLength) {
throw new Exception('数据异常:返回大小和写入大小不一致!');
}
$sByte = $eByte + 1;
$eByte = $sByte + $this->burstBytes;
$fileSize = $fileSize + $saveRes;
}
}
}
private function curl($range)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->siteUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HEADER, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_RANGE, $range);
if ($this->header) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->header);
}
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception('下载文件异常:' . curl_error($ch));
}
$headSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$httpHeader = substr($response, 0, $headSize);
if (!$httpHeader) {
throw new Exception('下载文件异常:未获取到响应头');
}
$fileStream = substr($response, $headSize);
$length = $this->getResHeaderValue('Content-Length', $httpHeader);
$httpCode = $this->getResHeaderValue('Http-Code', $httpHeader);
curl_close($ch);
return [
'code' => $httpCode,
'length' => $length,
'stream' => $fileStream,
];
}
private function getResHeaderValue($key, $responseHead)
{
$value = '';
$headArr = explode("\r\n", $responseHead);
foreach ($headArr as $loop) {
if ($key == 'Http-Code') {
if (preg_match('/HTTP\/1\.[0-9]{1} ([0-9]{3})/', $loop, $matches)) {
return $matches['1'];
}
} else {
if (strpos($loop, $key) !== false) {
$value = trim(str_replace($key . ':', '', $loop));
}
}
}
return $value;
}
}