php支持断点续传、分块下载的类


Posted in PHP onMay 02, 2016

本文是为大家分享php支持断点续传、分块下载的类,供大家参考,具体内容如下

<?php
 
/**
 * User: djunny
 * Date: 2016-04-29
 * Time: 17:18
 * Mail: 199962760@qq.com
 * 支持断点下载的类
 */
class downloader {
 
  /**
   * download file to local path
   *
   * @param    $url
   * @param    $save_file
   * @param int  $speed
   * @param array $headers
   * @param int  $timeout
   * @return bool
   * @throws Exception
   */
  static function get($url, $save_file, $speed = 10240, $headers = array(), $timeout = 10) {
    $url_info = self::parse_url($url);
    if (!$url_info['host']) {
      throw new Exception('Url is Invalid');
    }
 
    // default header
    $def_headers = array(
      'Accept'     => '*/*',
      'User-Agent'   => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
      'Accept-Encoding' => 'gzip, deflate',
      'Host'      => $url_info['host'],
      'Connection'   => 'Close',
      'Accept-Language' => 'zh-cn',
    );
 
    // merge heade
    $headers = array_merge($def_headers, $headers);
    // get content length
    $content_length = self::get_content_size($url_info['host'], $url_info['port'], $url_info['request'], $headers, $timeout);
 
    // content length not exist
    if (!$content_length) {
      throw new Exception('Content-Length is Not Exists');
    }
    // get exists length
    $exists_length = is_file($save_file) ? filesize($save_file) : 0;
    // get tmp data file
    $data_file = $save_file . '.data';
    // get tmp data
    $exists_data = is_file($data_file) ? json_decode(file_get_contents($data_file), 1) : array();
    // check file is valid
    if ($exists_length == $content_length) {
      $exists_data && @unlink($data_file);
      return true;
    }
    // check file is expire
    if ($exists_data['length'] != $content_length || $exists_length > $content_length) {
      $exists_data = array(
        'length' => $content_length,
      );
    }
    // write exists data
    file_put_contents($data_file, json_encode($exists_data));
 
    try {
      $download_status = self::download_content($url_info['host'], $url_info['port'], $url_info['request'], $save_file, $content_length, $exists_length, $speed, $headers, $timeout);
      if ($download_status) {
        @unlink($data_file);
      }
    } catch (Exception $e) {
      throw new Exception($e->getMessage());
    }
    return true;
  }
 
  /**
   * parse url
   *
   * @param $url
   * @return bool|mixed
   */
  static function parse_url($url) {
    $url_info = parse_url($url);
    if (!$url_info['host']) {
      return false;
    }
    $url_info['port']  = $url_info['port'] ? $url_info['host'] : 80;
    $url_info['request'] = $url_info['path'] . ($url_info['query'] ? '?' . $url_info['query'] : '');
    return $url_info;
  }
 
  /**
   * download content by chunk
   *
   * @param $host
   * @param $port
   * @param $url_path
   * @param $headers
   * @param $timeout
   */
  static function download_content($host, $port, $url_path, $save_file, $content_length, $range_start, $speed, &$headers, $timeout) {
    $request = self::build_header('GET', $url_path, $headers, $range_start);
    $fsocket = @fsockopen($host, $port, $errno, $errstr, $timeout);
    stream_set_blocking($fsocket, TRUE);
    stream_set_timeout($fsocket, $timeout);
    fwrite($fsocket, $request);
    $status = stream_get_meta_data($fsocket);
    if ($status['timed_out']) {
      throw new Exception('Socket Connect Timeout');
    }
    $is_header_end = 0;
    $total_size  = $range_start;
    $file_fp    = fopen($save_file, 'a+');
    while (!feof($fsocket)) {
      if (!$is_header_end) {
        $line = @fgets($fsocket);
        if (in_array($line, array("\n", "\r\n"))) {
          $is_header_end = 1;
        }
        continue;
      }
      $resp    = fread($fsocket, $speed);
      $read_length = strlen($resp);
      if ($resp === false || $content_length < $total_size + $read_length) {
        fclose($fsocket);
        fclose($file_fp);
        throw new Exception('Socket I/O Error Or File Was Changed');
      }
      $total_size += $read_length;
      fputs($file_fp, $resp);
      // check file end
      if ($content_length == $total_size) {
        break;
      }
      sleep(1);
      // for test
      //break;
    }
    fclose($fsocket);
    fclose($file_fp);
    return true;
 
  }
 
  /**
   * get content length
   *
   * @param $host
   * @param $port
   * @param $url_path
   * @param $headers
   * @param $timeout
   * @return int
   */
  static function get_content_size($host, $port, $url_path, &$headers, $timeout) {
    $request = self::build_header('HEAD', $url_path, $headers);
    $fsocket = @fsockopen($host, $port, $errno, $errstr, $timeout);
    stream_set_blocking($fsocket, TRUE);
    stream_set_timeout($fsocket, $timeout);
    fwrite($fsocket, $request);
    $status = stream_get_meta_data($fsocket);
    $length = 0;
    if ($status['timed_out']) {
      return 0;
    }
    while (!feof($fsocket)) {
      $line = @fgets($fsocket);
      if (in_array($line, array("\n", "\r\n"))) {
        break;
      }
      $line = strtolower($line);
      // get location
      if (substr($line, 0, 9) == 'location:') {
        $location = trim(substr($line, 9));
        $url_info = self::parse_url($location);
        if (!$url_info['host']) {
          return 0;
        }
        fclose($fsocket);
        return self::get_content_size($url_info['host'], $url_info['port'], $url_info['request'], $headers, $timeout);
      }
      // get content length
      if (strpos($line, 'content-length:') !== false) {
        list(, $length) = explode('content-length:', $line);
        $length = (int)trim($length);
      }
    }
    fclose($fsocket);
    return $length;
 
  }
 
  /**
   * build header for socket
   *
   * @param   $action
   * @param   $url_path
   * @param   $headers
   * @param int $range_start
   * @return string
   */
  static function build_header($action, $url_path, &$headers, $range_start = -1) {
    $out = $action . " {$url_path} HTTP/1.0\r\n";
    foreach ($headers as $hkey => $hval) {
      $out .= $hkey . ': ' . $hval . "\r\n";
    }
    if ($range_start > -1) {
      $out .= "Accept-Ranges: bytes\r\n";
      $out .= "Range: bytes={$range_start}-\r\n";
    }
    $out .= "\r\n";
 
    return $out;
  }
}
 
 
#use age
/*
try {
  if (downloader::get('http://dzs.aqtxt.com/files/11/23636/201604230358308081.rar', 'test.rar')) {
    //todo
    echo 'Download Succ';
  }
} catch (Exception $e) {
  echo 'Download Failed';
}
*/
?>

以上就是本文的全部内容,希望对大家的学习有所帮助。

PHP 相关文章推荐
php设计模式 Strategy(策略模式)
Jun 26 PHP
深入探讨<br />和 \r\n两者有什么区别??
Jun 05 PHP
PHP中对缓冲区的控制实现代码
Sep 29 PHP
php 表单提交大量数据发生丢失的解决方法
Mar 03 PHP
PHP正则表达式替换站点关键字链接后空白的解决方法
Sep 16 PHP
php实现发送微信模板消息的方法
Mar 07 PHP
使用Appcan客户端自动更新PHP版本号(全)
Jul 31 PHP
php二维码生成
Oct 19 PHP
PHP5.4起内置web服务器使用方法
Aug 09 PHP
Laravel核心解读之异常处理的实践过程
Feb 24 PHP
PHP常见过waf webshell以及最简单的检测方法
May 21 PHP
PHP安装扩展mcrypt以及相关依赖项深入讲解
Mar 04 PHP
php数组分页实现方法
Apr 30 #PHP
thinkPHP使用pclzip打包备份mysql数据库的方法
Apr 30 #PHP
php打包压缩文件之ZipArchive方法用法分析
Apr 30 #PHP
php使用pclzip类实现文件压缩的方法(附pclzip类下载地址)
Apr 30 #PHP
php简单实现数组分页的方法
Apr 30 #PHP
php简单创建zip压缩文件的方法
Apr 30 #PHP
Yii2 rbac权限控制操作步骤实例教程
Apr 29 #PHP
You might like
PHP simple_html_dom.php+正则 采集文章代码
2009/12/24 PHP
php中获取关键词及所属来源搜索引擎名称的代码
2011/02/15 PHP
php文件上传类完整实例
2016/05/14 PHP
PHP实现中国公民身份证号码有效性验证示例代码
2017/05/03 PHP
基于php数组中的索引数组和关联数组详解
2018/03/12 PHP
php设计模式之正面模式实例分析【星际争霸游戏案例】
2020/03/24 PHP
仿猪八戒网左下角的文字滚动效果
2011/10/28 Javascript
document.documentElement的一些使用技巧
2013/04/18 Javascript
扩展JS Date对象时间格式化功能的小例子
2013/12/02 Javascript
使用jquery animate创建平滑滚动效果(可以是到顶部、到底部或指定地方)
2014/05/27 Javascript
JavaScript异步回调的Promise模式封装实例
2014/06/07 Javascript
jQuery实现瀑布流的取巧做法分享
2015/01/12 Javascript
jQuery使用before()和after()在元素前后添加内容的方法
2015/03/26 Javascript
javascript实现画不相交的圆
2015/04/07 Javascript
详解JavaScript中localStorage使用要点
2016/01/13 Javascript
使用node+vue.js实现SPA应用
2016/01/28 Javascript
jQuery获取元素父节点的方法
2016/06/21 Javascript
jQuery插件EasyUI获取当前Tab中iframe窗体对象的方法
2016/08/05 Javascript
javascript跨域请求包装函数与用法示例
2016/11/03 Javascript
JS通过调用微信API实现微信支付功能的方法示例
2017/06/29 Javascript
ionic3实战教程之随机布局瀑布流的实现方法
2017/12/28 Javascript
详解easyui基于 layui.laydate日期扩展组件
2018/07/18 Javascript
vue使用原生js实现滚动页面跟踪导航高亮的示例代码
2018/10/25 Javascript
vue-router的两种模式的区别
2019/05/30 Javascript
用不到50行的Python代码构建最小的区块链
2017/11/16 Python
python调用百度语音识别api
2018/08/30 Python
Python3.6简单的操作Mysql数据库的三个实例
2018/10/17 Python
Python中的heapq模块源码详析
2019/01/08 Python
python实现比对美团接口返回数据和本地mongo数据是否一致示例
2019/08/09 Python
详解python中*号的用法
2019/10/21 Python
python编写实现抽奖器
2020/09/10 Python
html5-Canvas可以在web中绘制各种图形
2012/12/26 HTML / CSS
销售人员自我评价
2014/02/01 职场文书
写自荐信的注意事项
2014/03/09 职场文书
研究生求职自荐书
2014/06/23 职场文书
会计试用期自我评价
2015/03/10 职场文书