php 远程下载大文件,php下载远程文件(支持断点续传,支持超大文件)
斷點下載的原理:http請求頭添加Range參數告訴文件服務器端需要的字節范圍
例如1個文本文件的字節為1000,
第一次請求Range: bytes=0-500
第二次請求Range: bytes=501-1000
通過每次的請求將返回的流追加寫入到文件。
注意的項目:斷點下載服務器端的每次只返回字節傳輸的范圍的字節流,同時返回的狀態碼應該為206。
以下是我封裝的php下載遠程文件,可以通過命令行執行,也可以通過fpm執行,由于web服務器存在執行超時的問題,代碼中做了重復執行繼續斷點下載,超時后再次執行即可,解決大文件下載超時問題。例如我沒有服務器,只有虛擬主機,就可以用這個腳本來下載超大文件。<?php
class?download
{
/**
*?遠程文件的路徑
*?@var?string
*/
private?$siteUrl?=?'';
/**
*?分片下載大小
*?@var?int
*/
private?$burstBytes?=?2048;
/**
*?設置遠程下載文件的路徑
*?@param?string?$url?遠程文件的路徑
*?@return?$this
*/
public?function?setUrl($url)
{
$this->siteUrl?=?$url;
return?$this;
}
/**
*?設置分段下載字節大小
*?@param?int?$byte?字節大小
*?@return?$this
*/
public?function?setBurst($byte)
{
$this->burstBytes?=?$byte;
return?$this;
}
/**
*?獲取遠程文件的信息
*?@return?array
*?@throws?Exception
*/
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']))
{
//處理文件下載302問題
$this->siteUrl?=?$responseHeader['Location'];
return?$this->getSiteFiLeInfo();
}
return?$responseHeader;
}
/**
*?保存文件到本地
*?@param?string?$fileName?保存到本地的文件名稱
*?@throws
*/
public?function?saveFile($fileName)
{
//獲取遠程文件的信息
$siteFileInfo?=?$this->getSiteFiLeInfo();
$siteFileLength?=?$siteFileInfo['Content-Length']????0;
//根據文件是否存在創建文件句柄、計算斷點下載開始字節
$fd?=?null;
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)
{
throw?new?Exception('原文件已下載完成,請勿重復下載!');
}
//計算斷點下載結束字節
$sByte?=?$fileSize;
$eByte?=?$sByte?+?$this->burstBytes;
//循環下載文件
while?(true)
{
//文件下載完成
if?($fileSize?>=?$siteFileLength)
{
fclose($fd);
break;
}
//傳遞分片范圍
$xRange?=?"{$sByte}-$eByte";
//請求curl
$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)
{
file_put_contents('log.txt',?$xRange?.?PHP_EOL,?FILE_APPEND);
$saveRes?=?fwrite($fd,?$streamContent);
if?(!$saveRes)
{
throw?new?Exception('寫入流到文件失敗!');
}
if?($saveRes?!=?$streamLength)
{
//講道理這種情況基本不會遇到,除非分段數設置過大,暫時未做兼容處理,重新執行就行
throw?new?Exception('數據異常:返回大小和寫入大小不一致!');
}
//遞增range
$sByte?=?$eByte?+?1;
$eByte?=?$sByte?+?$this->burstBytes;
//記錄文件大小
$fileSize?=?$fileSize?+?$saveRes;
}
}
}
/**
*?獲取下載文件流
*?@param?string?$range?分片字節范圍
*?@param?array?$header?Http請求頭
*?@return?array
*?@throws
*/
private?function?curl($range,?$header?=?[])
{
$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);
//設置關閉SSL
curl_setopt($ch,?CURLOPT_SSL_VERIFYPEER,?0);
curl_setopt($ch,?CURLOPT_SSL_VERIFYHOST,?0);
//設置分片
curl_setopt($ch,?CURLOPT_RANGE,?$range);
//設置header
if?($header)
{
curl_setopt($ch,?CURLOPT_HTTPHEADER,?$header);
}
//執行請求
$response?=?curl_exec($ch);
if?(curl_errno($ch))
{
throw?new?Exception('下載文件異常:'?.?curl_error($ch));
}
//提取response_header和response_body
$headSize?=?curl_getinfo($ch,?CURLINFO_HEADER_SIZE);
$httpHeader?=?substr($response,?0,?$headSize);
if?(!$httpHeader)
{
throw?new?Exception('下載文件異常:未獲取到響應頭');
}
$fileStream?=?substr($response,?$headSize);
//解析header
$length?=?$this->getResHeaderValue('Content-Length',?$httpHeader);
$httpCode?=?$this->getResHeaderValue('Http-Code',?$httpHeader);
curl_close($ch);
//返回
return?[
'code'?=>?$httpCode,
'length'?=>?$length,
'stream'?=>?$fileStream,
];
}
/**
*?獲取響應頭某個Key的值
*?@param?string?$key?header頭的key
*?@param?string?$responseHead?header頭字符串
*?@return?string
*/
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;
}
}
//設置下載文件的url
$url?=?'https://www.gaojiufeng.cn/demo/1.tar.gz';
//設置分段下載的字節大小
$burst?=?4048;
//設置保存到服務器本地的文件名
$filename?=?'11.tar.gz';
try
{
//初始化下載器
$download?=?new?download();
//開始下載
$download->setUrl($url)->setBurst($burst)->saveFile($filename);
}
catch?(Exception?$exception)
{
var_dump($exception->getMessage());
}
腳本是單進程下載文件,如果你愿意可以修改為curl批量請求,每個請求執行一個范圍,保存為多個文件,最后合并文件即可。
總結
以上是生活随笔為你收集整理的php 远程下载大文件,php下载远程文件(支持断点续传,支持超大文件)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我的世界python俄罗斯方块手机版下载
- 下一篇: PHP创建圆柱体的类,创建一个类