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 设计模式之 工厂模式
Dec 19 PHP
windows环境下php配置memcache的具体操作步骤
Jun 09 PHP
php中如何同时使用session和cookie来保存用户登录信息
Jul 05 PHP
php 字符串压缩方法比较示例
Jan 23 PHP
PHP中鲜为人知的10个函数
Feb 28 PHP
php实现遍历目录并删除指定文件中指定内容
Jan 21 PHP
PHP数据库操作Helper类完整实例
May 11 PHP
Django中的cookie与session操作实例代码
Aug 17 PHP
laravel5.4利用163邮箱发送邮件的步骤详解
Sep 22 PHP
phpStudy2016 配置多个域名期间遇到的问题小结
Oct 19 PHP
浅谈PHP封装CURL
Mar 06 PHP
php 中self,this的区别和操作方法实例分析
Nov 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
web目录下不应该存在多余的程序(安全考虑)
2012/05/09 PHP
php实现的短网址算法分享
2014/06/20 PHP
php使用strip_tags()去除html标签仍有空白的解决方法
2016/07/28 PHP
Laravel框架实现多个视图共享相同数据的方法详解
2019/07/09 PHP
基于Web标准的UI组件 — 树状菜单(2)
2006/09/18 Javascript
jQuery获取Select选择的Text和Value(详细汇总)
2013/01/25 Javascript
巧用js提交表单轻松解决一个页面有多个提交按钮
2013/11/17 Javascript
调试JavaScript中正则表达式中遇到的问题
2015/01/27 Javascript
javascript中eval函数用法分析
2015/04/25 Javascript
AngularJS实用开发技巧(推荐)
2016/07/13 Javascript
微信小程序开发之大转盘 仿天猫超市抽奖实例
2016/12/08 Javascript
JS基于正则实现数字千分位用逗号分隔的方法
2017/06/16 Javascript
详解Vue学习笔记进阶篇之列表过渡及其他
2017/07/17 Javascript
详解webpack-dev-server使用方法
2018/09/14 Javascript
浅谈angular表单提交中ng-submit的默认使用方法
2018/09/30 Javascript
vue-cli3配置与跨域处理方法
2019/08/17 Javascript
python使用pil生成缩略图的方法
2015/03/26 Python
Python入门学习之字符串与比较运算符
2015/10/12 Python
Python中查看文件名和文件路径
2017/03/31 Python
Python如何实现MySQL实例初始化详解
2017/11/06 Python
python基础练习之几个简单的游戏
2017/11/10 Python
python 实现在Excel末尾增加新行
2018/05/02 Python
基于python实现把json数据转换成Excel表格
2020/05/07 Python
pycharm设置默认的UTF-8编码模式的方法详解
2020/06/01 Python
德国化妆品和天然化妆品网上商店:kosmetikfuchs.de
2017/06/09 全球购物
Whittard官方海外旗舰店:英国百年茶叶品牌
2018/02/22 全球购物
JackJones官方旗舰店:杰克琼斯男装
2018/03/27 全球购物
小学生手册家长评语
2014/04/16 职场文书
读书活动总结
2014/04/28 职场文书
超市开业庆典活动策划方案
2014/09/15 职场文书
读后感作文评语
2014/12/25 职场文书
作文评语怎么写
2014/12/25 职场文书
城管个人总结
2015/02/28 职场文书
2015年转正工作总结范文
2015/04/02 职场文书
名人传读书笔记
2015/06/26 职场文书
MySQL创建高性能索引的全步骤
2021/05/02 MySQL