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数据库连接时容易出错的特殊符号问题
Sep 01 PHP
php操作JSON格式数据的实现代码
Dec 24 PHP
解析PHP SPL标准库的用法(遍历目录,查找固定条件的文件)
Jun 18 PHP
php中3种方法删除字符串中间的空格
Mar 10 PHP
php中session过期时间设置及session回收机制介绍
May 05 PHP
CI框架中cookie的操作方法分析
Dec 12 PHP
smarty模板引擎从配置文件中获取数据的方法
Jan 22 PHP
php自定义分页类完整实例
Dec 25 PHP
Yii2.0多文件上传实例说明
Jul 24 PHP
PHP多进程通信-消息队列使用
Mar 08 PHP
PHP开启目录引索+fancyindex漂亮目录浏览带搜索功能
Sep 23 PHP
PHP实现随机发扑克牌
Apr 22 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生成静态页面详解
2006/12/05 PHP
php session 检测和注销
2009/03/16 PHP
js刷新框架子页面的七种方法代码
2008/11/20 Javascript
extJs 常用到的增,删,改,查操作代码
2009/12/28 Javascript
javascript下判断一个对象是否具有指定名称的属性的的代码
2010/01/11 Javascript
javascript针对DOM的应用分析(二)
2012/04/15 Javascript
setTimeout()递归调用不加引号出错的解决方法
2014/09/05 Javascript
使用jquery动态加载js文件的方法
2014/12/24 Javascript
跟我学习javascript的for循环和for...in循环
2015/11/18 Javascript
jQuery实现的简单提示信息插件
2015/12/08 Javascript
三个js循环的关键字示例(for与while)
2016/02/16 Javascript
javascript实现Java中的Map对象功能的实例详解
2017/08/21 Javascript
webpack 如何解析代码模块路径的实现
2019/09/04 Javascript
微信小程序实现蓝牙打印
2019/09/23 Javascript
layui 数据表格 根据值(1=业务,2=机构)显示中文名称示例
2019/10/26 Javascript
js+css实现全屏侧边栏
2020/06/16 Javascript
Python使用ftplib实现简易FTP客户端的方法
2015/06/03 Python
Python中的字符串查找操作方法总结
2016/06/27 Python
python3+PyQt5实现使用剪贴板做复制与粘帖示例
2017/01/24 Python
Python中matplotlib中文乱码解决办法
2017/05/12 Python
30秒轻松实现TensorFlow物体检测
2018/03/14 Python
Python设计模式之解释器模式原理与用法实例分析
2019/01/10 Python
Python学习笔记之文件的读写操作实例分析
2019/08/07 Python
基于Numba提高python运行效率过程解析
2020/03/02 Python
QML用PathView实现轮播图
2020/06/03 Python
python制作一个简单的gui 数据库查询界面
2020/11/19 Python
python模拟点击玩游戏的实例讲解
2020/11/26 Python
Foot Locker英国官网:美国知名运动产品零售商
2019/02/21 全球购物
不用游标的SQL语句有哪些
2012/09/07 面试题
MYSQL支持事务吗
2013/08/09 面试题
毕业生教师求职信
2013/10/20 职场文书
文秘自荐信
2014/06/28 职场文书
2014年客房服务员工作总结
2014/11/18 职场文书
装配车间主任岗位职责
2015/04/08 职场文书
oracle连接ODBC sqlserver数据源的详细步骤
2021/07/25 Oracle
python数字图像处理之对比度与亮度调整示例
2022/06/28 Python