php使用curl下載指定大小的文件實(shí)例代碼
php中使用基于libcurl的curl函數(shù),可以對目標(biāo)url發(fā)起http請求并獲取返回的響應(yīng)內(nèi)容。通常的請求方式類似如下的代碼:
public function callFunction($url, $postData, $method, header='')
{
$maxRetryTimes = 3;
$curl = curl_init();
/******初始化請求參數(shù)start******/
if(strtoupper($method) !== 'GET' && $postData){
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($postData));
}elseif (strtoupper($method) === 'GET' && $postData){
$url .= '?'. http_build_query($postData);
}
/******初始化請求參數(shù)end******/
curl_setopt_array($curl, array(
CURLOPT_URL => $url,
CURLOPT_TIMEOUT => 10,
CURLOPT_NOBODY => 0,
CURLOPT_RETURNTRANSFER => 1
));
if(method == 'POST'){
curl_setopt($curl, CURLOPT_POST, true);
}
if(false == empty()){
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
}
$response = false;
while(($response === false) && (--$maxRetryTimes > 0)){
$response = trim(curl_exec($curl));
}
return $response;
}
上面代碼中的這個$response是curl發(fā)起的這次http請求從$url獲取到的數(shù)據(jù),如果沒有在$header中通過range來指定要下載的大小,無論這個資源多大,那么都要請求完整的并返回的是這個URI的完整內(nèi)容。通常只用curl來請求求一些接口或者遠(yuǎn)程調(diào)用一個函數(shù)獲取數(shù)據(jù),,所以這個場景下CURLOPT_TIMEOUT這個參數(shù)很重要。
對于curl的使用場景不止訪問數(shù)據(jù)接口,還要對任意的url資源進(jìn)行檢測是否能提供正確的http服務(wù)。當(dāng)用戶填入的url是一個資源文件時,例如一個pdf或者ppt之類的,這時候如果網(wǎng)絡(luò)狀況較差的情況下用curl請求較大的資源,將不可避免的出現(xiàn)超時或者耗費(fèi)更多的網(wǎng)絡(luò)資源。之前的策略是完全下載(curl會下載存儲在內(nèi)存中),請求完后檢查內(nèi)容大小,當(dāng)超過目標(biāo)值就把這個監(jiān)控的任務(wù)暫停。這樣事發(fā)后限制其實(shí)治標(biāo)不治本,終于客戶提出了新的需求,不能停止任務(wù)只下載指定大小的文件并返回md5值由客戶去校驗(yàn)正確性。
經(jīng)過了一些嘗試,解決了這個問題,記錄過程如下文。
1、嘗試使用 CURLOPT_MAXFILESIZE。
對php和libcurl的版本有版本要求,完全的事前處理,當(dāng)發(fā)現(xiàn)目標(biāo)大于設(shè)置時,直接返回了超過大小限制的錯誤而不去下載目標(biāo)了,不符合要求。
2、使用curl下載過程的回調(diào)函數(shù)。
參考 http://php.net/manual/en/function.curl-setopt-array.php ,最終使用了CURLOPT_WRITEFUNCTION參數(shù)設(shè)置了on_curl_write,該函數(shù)將會1s中被回調(diào)1次。
$ch = curl_init(); $options = array(CURLOPT_URL => 'http://www.php.net/', CURLOPT_HEADER => false, CURLOPT_HEADERFUNCTION => 'on_curl_header', CURLOPT_WRITEFUNCTION => 'on_curl_write' );
最終我的實(shí)現(xiàn)片段:
function on_curl_write($ch, $data)
{
$pid = getmypid();
$downloadSizeRecorder = DownloadSizeRecorder::getInstance($pid);
$bytes = strlen($data);
$downloadSizeRecorder->downloadData .= $data;
$downloadSizeRecorder->downloadedFileSize += $bytes;
// error_log(' on_curl_write '.$downloadSizeRecorder->downloadedFileSize." > {$downloadSizeRecorder->maxSize} \n", 3, '/tmp/hyb.log');
//確保已經(jīng)下載的內(nèi)容略大于最大限制
if (($downloadSizeRecorder->downloadedFileSize - $bytes) > $downloadSizeRecorder->maxSize) {
return false;
}
return $bytes; //這個不正確的返回,將會報(bào)錯,中斷下載 "errno":23,"errmsg":"Failed writing body (0 != 16384)"
}
DownloadSizeRecorder是一個單例模式的類,curl下載時記錄大小,實(shí)現(xiàn)返回下載內(nèi)容的md5等。
class DownloadSizeRecorder
{
const ERROR_FAILED_WRITING = 23; //Failed writing body
public $downloadedFileSize;
public $maxSize;
public $pid;
public $hasOverMaxSize;
public $fileFullName;
public $downloadData;
private static $selfInstanceList = array();
public static function getInstance($pid)
{
if(!isset(self::$selfInstanceList[$pid])){
self::$selfInstanceList[$pid] = new self($pid);
}
return self::$selfInstanceList[$pid];
}
private function __construct($pid)
{
$this->pid = $pid;
$this->downloadedFileSize = 0;
$this->fileFullName = '';
$this->hasOverMaxSize = false;
$this->downloadData = '';
}
/**
* 保存文件
*/
public function saveMaxSizeData2File(){
if(empty($resp_data)){
$resp_data = $this->downloadData;
}
$fileFullName = '/tmp/http_'.$this->pid.'_'.time()."_{$this->maxSize}.download";
if($resp_data && strlen($resp_data)>0)
{
list($headerOnly, $bodyOnly) = explode("\r\n\r\n", $resp_data, 2);
$saveDataLenth = ($this->downloadedFileSize < $this->maxSize) ? $this->downloadedFileSize : $this->maxSize;
$needSaveData = substr($bodyOnly, 0, $saveDataLenth);
if(empty($needSaveData)){
return;
}
file_put_contents($fileFullName, $needSaveData);
if(file_exists($fileFullName)){
$this->fileFullName = $fileFullName;
}
}
}
/**
* 返回文件的md5
* @return string
*/
public function returnFileMd5(){
$md5 = '';
if(file_exists($this->fileFullName)){
$md5 = md5_file($this->fileFullName);
}
return $md5;
}
/**
* 返回已下載的size
* @return int
*/
public function returnSize(){
return ($this->downloadedFileSize < $this->maxSize) ? $this->downloadedFileSize : $this->maxSize;
}
/**
* 刪除下載的文件
*/
public function deleteFile(){
if(file_exists($this->fileFullName)){
unlink($this->fileFullName);
}
}
}
curl請求的代碼實(shí)例中,實(shí)現(xiàn)限制下載大小
…… curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'on_curl_write');//設(shè)置回調(diào)函數(shù) …… $pid = getmypid(); $downloadSizeRecorder = DownloadSizeRecorder::getInstance($pid); $downloadSizeRecorder->maxSize = $size_limit; …… //發(fā)起curl請求 $response = curl_exec($ch); …… //保存文件,返回md5 $downloadSizeRecorder->saveMaxSizeData2File(); //保存 $downloadFileMd5 = $downloadSizeRecorder->returnFileMd5(); $downloadedfile_size = $downloadSizeRecorder->returnSize(); $downloadSizeRecorder->deleteFile();
到這里,踩了一個坑。增加了on_curl_write后,$response會返回true,導(dǎo)致后面取返回內(nèi)容的時候異常。好在已經(jīng)實(shí)時限制了下載的大小,用downloadData來記錄了已經(jīng)下載的內(nèi)容,直接可以使用。
if($response === true){
$response = $downloadSizeRecorder->downloadData;
}
總結(jié)
以上所述是小編給大家介紹的php使用curl下載指定大小的文件,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
- php使用curl實(shí)現(xiàn)ftp文件下載功能
- php使用curl模擬瀏覽器表單上傳文件或者圖片的方法
- PHP使用curl請求實(shí)現(xiàn)post方式上傳圖片文件功能示例
- PHP實(shí)現(xiàn)通過CURL上傳文件功能示例
- 可兼容php5與php7的cURL文件上傳功能實(shí)例分析
- PHP5.0~5.6 各版本兼容性cURL文件上傳功能實(shí)例分析
- PHP使用curl模擬post上傳及接收文件的方法
- php curl 上傳文件代碼實(shí)例
- php下利用curl判斷遠(yuǎn)程文件是否存在的實(shí)現(xiàn)代碼
- PHP使用CURL實(shí)現(xiàn)下載文件功能示例
相關(guān)文章
解決PHPstudy Apache無法啟動的問題【親測有效】
這篇文章主要介紹了PHPstudy Apache無法啟動的問題及解決方法【親測有效】,本文給大家總結(jié)了三種方法供大家參考,需要的朋友可以參考下2020-10-10
php-fpm重啟導(dǎo)致的程序執(zhí)行中斷問題詳解
這篇文章主要給大家介紹了關(guān)于php-fpm重啟導(dǎo)致的程序執(zhí)行中斷問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
php版微信公眾平臺實(shí)現(xiàn)預(yù)約提交后發(fā)送email的方法
這篇文章主要介紹了php版微信公眾平臺實(shí)現(xiàn)預(yù)約提交后發(fā)送email的方法,結(jié)合實(shí)例形式分析了php微信公眾平臺郵件發(fā)送的相關(guān)操作技巧,需要的朋友可以參考下2016-09-09
PHP網(wǎng)頁游戲?qū)W習(xí)之Xnova(ogame)源碼解讀(五)
這篇文章主要介紹了PHP網(wǎng)頁游戲Xnova(ogame)源碼解讀游戲主界面,需要的朋友可以參考下2014-06-06
Drupal7連接多個數(shù)據(jù)庫及常見問題解決
這篇文章主要介紹了Drupal7連接多個數(shù)據(jù)庫的方法、操作實(shí)例,以及常見問題解決方法,需要的朋友可以參考下2014-03-03
php+js實(shí)現(xiàn)異步圖片上傳實(shí)例分享
本來想用插件上傳圖片的,后來自己寫了一個簡單的js實(shí)現(xiàn)異步的圖片上傳,不多說上代碼非常簡單2014-06-06
PHP中類靜態(tài)調(diào)用和范圍解析操作符的區(qū)別解析
這篇文章主要介紹了PHP中類靜態(tài)調(diào)用和范圍解析操作符的區(qū)別,需要的朋友可以參考下2018-01-01

