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下载过程的回调函数。
参考 ,最终使用了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 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来记录了已经下载的内容,直接可以使用。
if($response === true){ $response = $downloadsizerecorder->downloaddata; }
总结
以上所述是小编给大家介绍的php使用curl下载指定大小的文件,希望对大家有所帮助