断点下载的原理:http请求头添加Range参数告诉文件服务器端需要的字节范围

例如1个文本文件的字节为1000,

第一次请求Range: bytes=0-500

第二次请求Range: bytes=501-1000

通过每次的请求将返回的流追加写入到文件。

注意的项目:断点下载服务器端的每次只返回字节传输的范围的字节流,同时返回的状态码应该为206。

以下是我封装的php下载远程文件,可以通过命令行执行,也可以通过fpm执行,由于web服务器存在执行超时的问题,代码中做了重复执行继续断点下载,超时后再次执行即可,解决大文件下载超时问题。例如我没有服务器,只有虚拟主机,就可以用这个脚本来下载超大文件。<?php

class download

{

/**

* 远程文件的路径

* @var string

*/

private $siteUrl = '';

/**

* 分片下载大小

* @var int

*/

private $burstBytes = 2048;

/**

* 设置远程下载文件的路径

* @param string $url 远程文件的路径

* @return $this

*/

public function setUrl($url)

{

$this->siteUrl = $url;

return $this;

}

/**

* 设置分段下载字节大小

* @param int $byte 字节大小

* @return $this

*/

public function setBurst($byte)

{

$this->burstBytes = $byte;

return $this;

}

/**

* 获取远程文件的信息

* @return array

* @throws Exception

*/

private function getSiteFiLeInfo()

{

if (!$this->siteUrl)

{

throw new Exception('请先设置远程文件url!');

}

$responseHeader = get_headers($this->siteUrl, 1);

if (!$responseHeader)

{

throw new Exception('获取远程文件信息失败!');

}

if (!empty($responseHeader['Location']))

{

//处理文件下载302问题

$this->siteUrl = $responseHeader['Location'];

return $this->getSiteFiLeInfo();

}

return $responseHeader;

}

/**

* 保存文件到本地

* @param string $fileName 保存到本地的文件名称

* @throws

*/

public function saveFile($fileName)

{

//获取远程文件的信息

$siteFileInfo = $this->getSiteFiLeInfo();

$siteFileLength = $siteFileInfo['Content-Length'] ?? 0;

//根据文件是否存在创建文件句柄、计算断点下载开始字节

$fd = null;

if (file_exists($fileName))

{

$fd = fopen($fileName, 'ab');

}

else

{

$fd = fopen($fileName, 'wb');

}

if (!$fd)

{

throw new Exception('创建或打开本地文件失败!');

}

//加上文件锁,防止刷新抢占资源句柄

if (!flock($fd, LOCK_EX | LOCK_NB))

{

throw new Exception('已有相关进程操作执行下载本文件!');

}

//检查文件是否已经下载完成

$fileSize = filesize($fileName);

if ($fileSize && $fileSize >= $siteFileLength)

{

throw new Exception('原文件已下载完成,请勿重复下载!');

}

//计算断点下载结束字节

$sByte = $fileSize;

$eByte = $sByte + $this->burstBytes;

//循环下载文件

while (true)

{

//文件下载完成

if ($fileSize >= $siteFileLength)

{

fclose($fd);

break;

}

//传递分片范围

$xRange = "{$sByte}-$eByte";

//请求curl

$result = $this->curl($xRange);

//检查是否正常请求

$code = $result['code'] ?? 0;

if (!$code)

{

throw new Exception('Http请求异常!');

}

if ($code != 206)

{

throw new Exception('Http状态码异常,可能不支持断点的资源或已完成下载!');

}

//返回流长度

$streamLength = $result['length'] ?? 0;

//返回流内容

$streamContent = $result['stream'] ?? '';

if ($streamLength > 0)

{

file_put_contents('log.txt', $xRange . PHP_EOL, FILE_APPEND);

$saveRes = fwrite($fd, $streamContent);

if (!$saveRes)

{

throw new Exception('写入流到文件失败!');

}

if ($saveRes != $streamLength)

{

//讲道理这种情况基本不会遇到,除非分段数设置过大,暂时未做兼容处理,重新执行就行

throw new Exception('数据异常:返回大小和写入大小不一致!');

}

//递增range

$sByte = $eByte + 1;

$eByte = $sByte + $this->burstBytes;

//记录文件大小

$fileSize = $fileSize + $saveRes;

}

}

}

/**

* 获取下载文件流

* @param string $range 分片字节范围

* @param array $header Http请求头

* @return array

* @throws

*/

private function curl($range, $header = [])

{

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $this->siteUrl);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');

curl_setopt($ch, CURLOPT_HEADER, TRUE);

//设置关闭SSL

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

//设置分片

curl_setopt($ch, CURLOPT_RANGE, $range);

//设置header

if ($header)

{

curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

}

//执行请求

$response = curl_exec($ch);

if (curl_errno($ch))

{

throw new Exception('下载文件异常:' . curl_error($ch));

}

//提取response_header和response_body

$headSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);

$httpHeader = substr($response, 0, $headSize);

if (!$httpHeader)

{

throw new Exception('下载文件异常:未获取到响应头');

}

$fileStream = substr($response, $headSize);

//解析header

$length = $this->getResHeaderValue('Content-Length', $httpHeader);

$httpCode = $this->getResHeaderValue('Http-Code', $httpHeader);

curl_close($ch);

//返回

return [

'code' => $httpCode,

'length' => $length,

'stream' => $fileStream,

];

}

/**

* 获取响应头某个Key的值

* @param string $key header头的key

* @param string $responseHead header头字符串

* @return string

*/

private function getResHeaderValue($key, $responseHead)

{

$value = '';

$headArr = explode("\r\n", $responseHead);

foreach ($headArr as $loop)

{

if ($key == 'Http-Code')

{

if (preg_match('/HTTP\/1\.[0-9]{1} ([0-9]{3})/', $loop, $matches))

{

return $matches['1'];

}

}

else

{

if (strpos($loop, $key) !== false)

{

$value = trim(str_replace($key . ':', '', $loop));

}

}

}

return $value;

}

}

//设置下载文件的url

$url = 'https://www.gaojiufeng.cn/demo/1.tar.gz';

//设置分段下载的字节大小

$burst = 4048;

//设置保存到服务器本地的文件名

$filename = '11.tar.gz';

try

{

//初始化下载器

$download = new download();

//开始下载

$download->setUrl($url)->setBurst($burst)->saveFile($filename);

}

catch (Exception $exception)

{

var_dump($exception->getMessage());

}

脚本是单进程下载文件,如果你愿意可以修改为curl批量请求,每个请求执行一个范围,保存为多个文件,最后合并文件即可。

php 远程下载大文件,php下载远程文件(支持断点续传,支持超大文件)相关推荐

  1. php文件断点续传,php超大文件及断点续传下载函数

    最近导出订单信息的时候出现一个php内存溢出的问题,原因是文件在下载的时候读取生成的临时文件过大,php内存无法容纳,开始是想更改php内存限制,但不是长久之计,于是就想到了把文件分次读取,并下载的方 ...

  2. 在解压缩某些文件时出现问题检查计算机上,解压缩超大文件时出现问题

    我已经得到了一些压缩的文件,但是解压是30GB+的,而且是在Windows中压缩的.我试图使用EC2实例创建一个系统来解压这些实例,但是我一直在耗尽内存(错误IOError: [Errno 28] N ...

  3. Unity 最新UnityWebRequest下载,同时显示下载进度,和 显示网速,今天贴出来和大家分享

    Unity 最新UnityWebRequest下载网络资源,支持断点续传.多文件同时下载,同时显示下载进度,和 显示网速,今天贴出来和大家分享 显示网速图片 附上案例链接 可下载 https://do ...

  4. 快速传超大文件的解决方案

    最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表 ...

  5. python第六篇:Python复制超大文件、复制二进制文件

    Python文件复制 # 写程序实现复制文件的功能 # 要求: # 1. 源文件路径和目标文件路径需要手动输入 # 2. 要考虑文件关闭的问题 # 3. 要考虑复制超大文件的问题 # 4. 要能复制二 ...

  6. gin 前端文件打包_远程URL文件批量下载打包的方法

    开始 最近代码重构遇到了一个问题,需要把OSS 上的一批图片打包下载 旧服务器的硬盘是直接挂载OSS,所以直接调的Linux系统命令复制打包,所以速度比较快.新服务器重构代码行不通,这样做也不好 查阅 ...

  7. 备份、文件分享、远程下载 海康Mage10轻NAS首发体验

    对于数码爱好者来说,NAS(Network Attached Storage 网络连接存储)是集家庭影音娱乐中心.个人数据存储与管理等多功能于一身的设备.虽然像群晖(Synology)与威联通(QNA ...

  8. python批量下载文件-python 从远程批量下载文件到本地

    需求: 1.从postgresql数据库中查出附件名称 2.从远程服务器下载对应的附件 用到的python模块paramiko.psycopg2. paramiko是用python写的一个模块,遵循S ...

  9. win7连接sftp_SFTP远程连接服务器上传下载文件-vs2013项目实例

    本项目仅测试远程连接服务器,支持上传,下载文件,更多功能开发请看API自行开发. 环境:win7系统,vs2013 vs2013项目实例下载地址:CSDN下载 如果没有CSDN积分,百度网盘下载(密码 ...

最新文章

  1. 好渴望 wacom Intuos3
  2. 函数不可访问_C++之访问控制与继承
  3. 手写自己的MyBatis框架-支持注解配置SQL
  4. Bzoj 4548: 小奇的糖果(双向链表+排序+树状数组)
  5. 指针和指针的指针_网络上的iPad指针
  6. javafx之TableView的TableColumn
  7. html转换pdf中文失败,解决html导出pdf中文乱码问题的正确姿势
  8. 【pytorch】常见的坑汇总
  9. 发展壮大:帮助独立游戏开发商解决分销难题
  10. 谷粒商城:16.商城业务 — 首页
  11. jdk TreeMap源码解析
  12. 面试题31:连续子数组的最大和
  13. 【转】What is an entity system framework for game development?
  14. win7开机动画_win7电脑修改开机动画的操作方法
  15. Word——如何统计除去标点符号的字数
  16. 使用苹果发布证书打包好的ipa如何安装到ios手机上测试
  17. 蓝桥杯2019 c/c++ B组真题
  18. 获取秒懂百科视频地址/获取百度百科视频地址
  19. 英语的加减乘除怎么计算机,用英语表示加减乘除法的用法
  20. 一个编得好的拼音输入法C51的

热门文章

  1. 1NF 2NF 3NF BCNF
  2. python爬虫编码转换_Python 爬虫遇到形如 小说 的编码如何转换为中文? - SegmentFault 思否...
  3. windows中mysql添加环境变量_windows 下添加mysql到系统环境变量
  4. 浙江义乌计算机中专学校,浙江义乌有没有中专学校?
  5. java excel 兼容问题_java--POI解析excel兼容性问题
  6. mac pip安装mysql_Mac pip安装mysql-python失败
  7. python如何输入多个数据并增加到一个列表里_python 将表格多个列数据放到同一个单元格中...
  8. bat 存储过程返回值_MySQL-存储过程和函数详述
  9. 锁 唤醒_Java笔记|等待唤醒机制
  10. 操作系统之进程管理:3、进程控制(进程状态转化的实现)、原语、进程通信(共享、管道、消息)