php使用curl下载指定大小的文件实例代码


Posted in PHP onSeptember 30, 2017

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 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下载指定大小的文件,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

PHP 相关文章推荐
图书管理程序(二)
Oct 09 PHP
ExtJS与PHP、MySQL实现存储的方法
Apr 02 PHP
解析MySql与Java的时间类型
Jun 22 PHP
php按百分比生成缩略图的代码分享
May 10 PHP
PHP实现清除wordpress里恶意代码
Oct 21 PHP
php基于单例模式封装mysql类完整实例
Oct 18 PHP
php实现生成带二维码图片并强制下载功能
Feb 24 PHP
Laravel框架实现简单的学生信息管理平台案例
May 07 PHP
Laravel框架模型的创建及模型对数据操作示例
May 07 PHP
php使用mysqli和pdo扩展,测试对比连接mysql数据库的效率完整示例
May 09 PHP
Laravel自定义 封装便捷返回Json数据格式的引用方法
Sep 29 PHP
swoole锁的机制代码实例讲解
Mar 04 PHP
ThinkPHP3.1.x修改成功与失败跳转页面的方法
Sep 29 #PHP
PHP 获取 ping 时间的实现方法
Sep 29 #PHP
使用PHP+MySql实现微信投票功能实例代码
Sep 29 #PHP
PHP使用PDO访问oracle数据库的步骤详解
Sep 29 #PHP
Laravel中批量赋值Mass-Assignment的真正含义详解
Sep 29 #PHP
WHOOPS PHP调试库的使用
Sep 29 #PHP
Laravel中获取路由参数Route Parameters的五种方法示例
Sep 29 #PHP
You might like
php radio 单选框获取与保持值的实现代码
2010/05/15 PHP
PHP CURL CURLOPT参数说明(curl_setopt)
2013/09/30 PHP
PHP常用的排序和查找算法
2015/08/06 PHP
symfony2.4的twig中date用法分析
2016/03/18 PHP
Javascript----文件操作
2007/01/18 Javascript
javascript Select标记中options操作方法集合
2008/10/22 Javascript
js刷新框架子页面的七种方法代码
2008/11/20 Javascript
Jquery中使用setInterval和setTimeout的方法
2013/04/08 Javascript
深入理解JavaScript系列(30):设计模式之外观模式详解
2015/03/03 Javascript
Bootstrap框架动态生成Web页面文章内目录的方法
2016/05/12 Javascript
jQuery控制div实现随滚动条滚动效果
2016/06/07 Javascript
jQuery实现订单提交页发送短信功能前端处理方法
2016/07/04 Javascript
js动态添加的DIV中的onclick事件简单实例
2016/07/25 Javascript
JS传参及动态修改页面布局
2017/04/13 Javascript
详解Next.js页面渲染的优化方案
2019/01/27 Javascript
js实现带有动画的返回顶部
2020/08/09 Javascript
详解datagrid使用方法(重要)
2020/11/06 Javascript
Python实现的几个常用排序算法实例
2014/06/16 Python
Python实现多行注释的另类方法
2014/08/22 Python
详解Python的Django框架中manage命令的使用与扩展
2016/04/11 Python
Python控制多进程与多线程并发数总结
2016/10/26 Python
python3个性签名设计实现代码
2018/06/19 Python
详解Django-restframework 之频率源码分析
2019/02/27 Python
Python使用微信接入图灵机器人过程解析
2019/11/04 Python
基于Python实现扑克牌面试题
2019/12/11 Python
numpy实现神经网络反向传播算法的步骤
2019/12/24 Python
详解python opencv、scikit-image和PIL图像处理库比较
2019/12/26 Python
python调用百度AI接口实现人流量统计
2021/02/03 Python
CSS3教程(1):什么是CSS3
2009/04/02 HTML / CSS
承诺书格式
2014/06/03 职场文书
党的群众路线对照检查材料
2014/08/27 职场文书
我的职业生涯规划:打造自己的运动帝国
2014/09/18 职场文书
关于对大人不礼貌的检讨书
2014/09/29 职场文书
安全生产月标语
2014/10/07 职场文书
2015年大学生暑期实习报告
2015/07/13 职场文书
 Redis 串行生成顺序编码的方法实现
2022/04/03 Redis