>  기사  >  백엔드 개발  >  PHP가 컬을 사용하여 지정된 크기의 파일을 다운로드하는 방법의 예

PHP가 컬을 사용하여 지정된 크기의 파일을 다운로드하는 방법의 예

黄舟
黄舟원래의
2017-10-16 09:46:172051검색

PHP의 libcurl 기반 컬 함수를 사용하면 대상 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는 $에서 범위로 지정되지 않은 경우 컬에 의해 시작된 이 http 요청의 $url에서 얻은 데이터입니다. 헤더 다운로드할 리소스의 크기에 관계없이 URI의 전체 콘텐츠를 요청하고 반환해야 합니다. 일반적으로 컬은 일부 인터페이스를 요청하거나 데이터를 얻기 위해 원격으로 함수를 호출하는 데만 사용되므로 이 시나리오에서는 CURLOPT_TIMEOUT 매개변수가 매우 중요합니다.

컬의 사용 시나리오는 데이터 인터페이스에 액세스하는 것뿐만 아니라 URL 리소스가 올바른 http 서비스를 제공할 수 있는지 여부를 감지하는 것입니다. 사용자가 입력한 URL이 pdf, ppt 등의 리소스 파일인 경우, 네트워크 상태가 좋지 않아 컬을 사용하여 더 큰 리소스를 요청하게 되면 필연적으로 타임아웃이 발생하거나 인터넷 리소스가 더 많이 소모됩니다. . 이전 전략은 완전히 다운로드하는 것이었습니다(curl은 이를 다운로드하여 메모리에 저장합니다). 요청이 완료된 후 콘텐츠 크기를 확인하여 대상 값을 초과하면 모니터링 작업이 일시 중지됩니다. 사고 이후의 이러한 제한은 실제로 근본 원인이 아닌 증상을 해결했습니다. 결국 고객은 작업을 중지할 수 없으며 지정된 크기의 파일만 다운로드되고 고객에게 md5 값이 반환되었습니다. 정확성을 확인하십시오.

몇번의 시도 끝에 이 문제는 해결되었습니다. 녹음 과정은 다음과 같습니다.

1. CURLOPT_MAXFILESIZE를 사용해 보세요.

PHP 및 libcurl 버전에는 버전 요구 사항이 있는데, 이는 완전히 전처리되어 대상이 설정보다 큰 것으로 확인되면 대상을 다운로드하지 않고 크기 제한을 초과하는 오류가 직접 반환되므로 충족되지 않습니다. 요구 사항.

2. 컬 다운로드 프로세스의 콜백 기능을 사용합니다.

http://php.net/manual/en/function.curl-setopt-array.php를 참조하고 마지막으로 CURLOPT_WRITEFUNCTION 매개변수를 사용하여 on_curl_write를 설정합니다. 이 함수는 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는 컬이 다운로드될 때 크기를 기록하고 다운로드한 콘텐츠의 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_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는 다운로드한 콘텐츠를 기록하는 데 사용되며 직접 사용할 수 있습니다.

rreee

위 내용은 PHP가 컬을 사용하여 지정된 크기의 파일을 다운로드하는 방법의 예의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.