ホームページ  >  記事  >  バックエンド開発  >  PHP がcurlを使用して指定されたサイズのファイルをダウンロードする方法の例

PHP がcurlを使用して指定されたサイズのファイルをダウンロードする方法の例

黄舟
黄舟オリジナル
2017-10-16 09:46:172074ブラウズ

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 = &#39;&#39;;        
    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, &#39;on_curl_write&#39;);//设置回调函数
……
$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 サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。