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 ダウンロードするリソースのサイズに関係なく、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 を設定します。この関数は 1 秒に 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 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リクエストのコード例では、ダウンロードサイズに制限があります
…… 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 はダウンロードされたコンテンツの記録に使用され、直接使用できます。
りー
以上がPHP がcurlを使用して指定されたサイズのファイルをダウンロードする方法の例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。