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中去掉字符串首尾空格的方法
May 19 PHP
注意:php5.4删除了session_unregister函数
Aug 05 PHP
php后台如何避免用户直接进入方法实例
Oct 15 PHP
php邮箱地址正则表达式验证
Nov 13 PHP
在openSUSE42.1下编译安装PHP7 的方法
Dec 24 PHP
PHP编写RESTful接口
Feb 23 PHP
PHP7新特性foreach 修改示例介绍
Aug 26 PHP
php获取ajax的headers方法与内容实例
Dec 27 PHP
Laravel 中创建 Zip 压缩文件并提供下载的实现方法
Apr 02 PHP
PHP常用工具函数小结【移除XSS攻击、UTF8与GBK编码转换等】
Apr 27 PHP
Laravel Eloquent ORM 实现查询表中指定的字段
Oct 17 PHP
php实现JWT(json web token)鉴权实例详解
Nov 05 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
实用函数5
2007/11/08 PHP
初次接触php抽象工厂模式(Elgg)
2010/03/21 PHP
thinkphp 多表 事务详解
2013/06/17 PHP
php自动加载autoload机制示例分享
2014/02/20 PHP
10个实用的PHP正则表达式汇总
2014/10/23 PHP
thinkphp实现like模糊查询实例
2014/10/29 PHP
详解 PHP加密解密字符串函数附源码下载
2015/12/18 PHP
Yii框架实现的验证码、登录及退出功能示例
2017/05/20 PHP
php报错502badgateway解决方法
2019/10/11 PHP
js 图片随机不定向浮动的实现代码
2013/07/02 Javascript
JavaScript中的getTimezoneOffset()方法使用详解
2015/06/10 Javascript
js图片翻书效果代码分享
2015/08/20 Javascript
js操作数组函数实例小结
2015/12/10 Javascript
使用node+vue.js实现SPA应用
2016/01/28 Javascript
Javascript 实现全屏滚动实例代码
2016/12/31 Javascript
BootStrap学习笔记之nav导航栏和面包屑导航
2017/01/03 Javascript
jQuery设置图片等比例缩小的方法
2017/04/29 jQuery
详解vue模拟加载更多功能(数据追加)
2017/06/23 Javascript
详解NodeJs开发微信公众号
2018/05/25 NodeJs
layui内置模块layim发送图片添加加载动画的方法
2019/09/23 Javascript
详解小程序如何动态绑定点击的执行方法
2019/11/26 Javascript
浅析Vue下的components模板使用及应用
2019/11/27 Javascript
[44:40]Spirit vs Navi Supermajor小组赛 A组败者组第一轮 BO3 第一场 6.2
2018/06/03 DOTA
在Python的Django框架中生成CSV文件的方法
2015/07/22 Python
在Python web中实现验证码图片代码分享
2017/11/09 Python
TensorFlow实现创建分类器
2018/02/06 Python
python如何为被装饰的函数保留元数据
2018/03/21 Python
python获取txt文件词向量过程详解
2019/07/05 Python
如何基于Python实现自动扫雷
2020/01/06 Python
Python3 字典dictionary入门基础附实例
2020/02/10 Python
Python3运算符常见用法分析
2020/02/14 Python
利用CSS3的transition属性实现滑动效果
2015/08/05 HTML / CSS
2015年公民道德宣传日活动总结
2015/03/23 职场文书
学习经验交流会演讲稿
2015/11/02 职场文书
SQL Server——索引+基于单表的数据插入与简单查询【1】
2021/04/05 SQL Server
用python修改excel表某一列内容的操作方法
2021/06/11 Python