php使用curl下载指定大小的文件
? ? php中使用基于libcurl的curl函數,可以對目標url發起http請求并獲取返回的響應內容。通常的請求方式類似如下的代碼:
public?function?callFunction($url,?$postData,?$method,?header='') {$maxRetryTimes?=?3;$curl?=?curl_init();/******初始化請求參數start******/if(strtoupper($method)?!==?'GET'?&&?$postData){curl_setopt($curl,?CURLOPT_POSTFIELDS,?json_encode($postData));}elseif?(strtoupper($method)?===?'GET'?&&?$postData){$url?.=?'?'.?http_build_query($postData);}/******初始化請求參數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發起的這次http請求從$url獲取到的數據,如果沒有在$header中通過range來指定要下載的大小,無論這個資源多大,那么都要請求完整的并返回的是這個URI的完整內容。通常只用curl來請求求一些接口或者遠程調用一個函數獲取數據,,所以這個場景下CURLOPT_TIMEOUT這個參數很重要。
? ? 對于curl的使用場景不止訪問數據接口,還要對任意的url資源進行檢測是否能提供正確的http服務。當用戶填入的url是一個資源文件時,例如一個pdf或者ppt之類的,這時候如果網絡狀況較差的情況下用curl請求較大的資源,將不可避免的出現超時或者耗費更多的網絡資源。之前的策略是完全下載(curl會下載存儲在內存中),請求完后檢查內容大小,當超過目標值就把這個監控的任務暫停。這樣事發后限制其實治標不治本,終于客戶提出了新的需求,不能停止任務只下載指定大小的文件并返回md5值由客戶去校驗正確性。
? ? 經過了一些嘗試,解決了這個問題,記錄過程如下文。
? ? 1、嘗試使用 CURLOPT_MAXFILESIZE。
對php和libcurl的版本有版本要求,完全的事前處理,當發現目標大于設置時,直接返回了超過大小限制的錯誤而不去下載目標了,不符合要求。
? ? 2、使用curl下載過程的回調函數。
? ? 參考http://php.net/manual/en/function.curl-setopt-array.php,最終使用了CURLOPT_WRITEFUNCTION參數設置了on_curl_write,該函數將會1s中被回調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' );? ? 最終我的實現片段:
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');//確保已經下載的內容略大于最大限制if?(($downloadSizeRecorder->downloadedFileSize?-?$bytes)?>?$downloadSizeRecorder->maxSize)?{return?false;}return?$bytes;??//這個不正確的返回,將會報錯,中斷下載?"errno":23,"errmsg":"Failed?writing?body?(0?!=?16384)" }? ? DownloadSizeRecorder是一個單例模式的類,curl下載時記錄大小,實現返回下載內容的md5等。
class?DownloadSizeRecorder {const?ERROR_FAILED_WRITING?=?23;?//Failed?writing?bodypublic?$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請求的代碼實例中,實現限制下載大小
…… curl_setopt($ch,?CURLOPT_WRITEFUNCTION,?'on_curl_write');//設置回調函數 …… $pid?=?getmypid(); $downloadSizeRecorder?=?DownloadSizeRecorder::getInstance($pid); $downloadSizeRecorder->maxSize?=?$size_limit; …… //發起curl請求 $response?=?curl_exec($ch); …… //保存文件,返回md5 $downloadSizeRecorder->saveMaxSizeData2File();??//保存 $downloadFileMd5?=?$downloadSizeRecorder->returnFileMd5(); $downloadedfile_size?=?$downloadSizeRecorder->returnSize(); $downloadSizeRecorder->deleteFile();? ? ?到這里,踩了一個坑。增加了on_curl_write后,$response會返回true,導致后面取返回內容的時候異常。好在已經實時限制了下載的大小,用downloadData來記錄了已經下載的內容,直接可以使用。
if($response?===?true){$response?=?$downloadSizeRecorder->downloadData; }轉載于:https://blog.51cto.com/byteh/1969809
總結
以上是生活随笔為你收集整理的php使用curl下载指定大小的文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: The 'Microsoft Jet O
- 下一篇: 一次PostgreSQL行估算偏差导致的