php 断点续传,PHP实现断点续传

断点续传指的是在上传/下载时,将任务(一个文件或压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传/下载,如果碰到网络故障,可以从已经上传/下载的部分开始继续上传/下载未完成的部分,而没有必要从头开始上传/下载。可以节省时间,提高速度。

实现原理

HTTP1.1 协议(RFC2616)开始支持获取文件的部分内容,这为并行下载以及断点续传提供了技术支持。它通过在 Header 里两个参数实现的,客户端发请求时对应的是 Range ,服务器端响应时对应的是 Content-Range。

Range

用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:

Range:(unit=first byte pos)-[last byte pos]

Range 头部的格式有以下几种情况:

Range: bytes=0-499 表示第 0-499 字节范围的内容

Range: bytes=500-999 表示第 500-999 字节范围的内容

Range: bytes=-500 表示最后 500 字节的内容

Range: bytes=500- 表示从第 500 字节开始到文件结束部分的内容

Range: bytes=0-0,-1 表示第一个和最后一个字节

Range: bytes=500-600,601-999 同时指定几个范围

Content-Range

用于响应头中,在发出带 Range 的请求后,服务器会在 Content-Range 头部返回当前接受的范围和文件总大小。一般格式:

Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]

例如:

Content-Range: bytes 0-499/22400

0-499 是指当前发送的数据的范围,而 22400 则是文件的总大小。

而在响应完成后,返回的响应头内容也不同:

HTTP/1.1 200 Ok(不使用断点续传方式)

HTTP/1.1 206 Partial Content(使用断点续传方式)

增强校验

在实际场景中,会出现一种情况,即在终端发起续传请求时,URL 对应的文件内容在服务器端已经发生变化,此时续传的数据肯定是错误的。如何解决这个问题了?显然此时需要有一个标识文件唯一性的方法。

在 RFC2616 中也有相应的定义,比如实现 Last-Modified 来标识文件的最后修改时间,这样即可判断出续传文件时是否已经发生过改动。同时 FC2616 中还定义有一个 ETag 的头,可以使用 ETag 头来放置文件的唯一标识。

Last-Modified

If-Modified-Since,和 Last-Modified 一样都是用于记录页面最后修改时间的 HTTP 头信息,只是 Last-Modified 是由服务器往客户端发送的 HTTP 头,而 If-Modified-Since 则是由客户端往服务器发送的头,可以看到,再次请求本地存在的 cache 页面时,客户端会通过 If-Modified-Since 头将先前服务器端发过来的 Last-Modified 最后修改时间戳发送回去,这是为了让服务器端进行验证,通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回新的内容,如果是最新的,则返回 304 告诉客户端其本地 cache 的页面是最新的,于是客户端就可以直接从本地加载页面了,这样在网络上传输的数据就会大大减少,同时也减轻了服务器的负担。

Etag

Etag(Entity Tags)主要为了解决 Last-Modified 无法解决的一些问题。

一些文件也许会周期性的更改,但是内容并不改变(仅改变修改时间),这时候我们并不希望客户端认为这个文件被修改了,而重新 GET。

某些文件修改非常频繁,例如:在秒以下的时间内进行修改(1s 内修改了 N 次),If-Modified-Since 能检查到的粒度是 s 级的,这种修改无法判断(或者说 UNIX 记录 MTIME 只能精确到秒)。

某些服务器不能精确的得到文件的最后修改时间。

为此,HTTP/1.1 引入了 Etag。Etag 仅仅是一个和文件相关的标记,可以是一个版本标记,例如:v1.0.0;或者说 “627-4d648041f6b80” 这么一串看起来很神秘的编码。但是 HTTP/1.1 标准并没有规定 Etag 的内容是什么或者说要怎么实现,唯一规定的是 Etag 需要放在 “” 内。

If-Range

用于判断实体是否发生改变,如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。一般格式:

If-Range: Etag | HTTP-Date

也就是说,If-Range 可以使用 Etag 或者 Last-Modified 返回的值。当没有 ETage 却有 Last-modified 时,可以把 Last-modified 作为 If-Range 字段的值。

例如:

If-Range: “627-4d648041f6b80”

If-Range: Fri, 22 Feb 2013 03:45:02 GMT

If-Range 必须与 Range 配套使用。如果请求报文中没有 Range,那么 If-Range 就会被忽略。如果服务器不支持 If-Range,那么 Range 也会被忽略。

如果请求报文中的 Etag 与服务器目标内容的 Etag 相等,即没有发生变化,那么应答报文的状态码为 206。如果服务器目标内容发生了变化,那么应答报文的状态码为 200。

用于校验的其他 HTTP 头信息:If-Match/If-None-Match、If-Modified-Since/If-Unmodified-Since。

工作原理

Etag 由服务器端生成,客户端通过 If-Range 条件判断请求来验证资源是否修改。请求一个文件的流程如下:

第一次请求:

客户端发起 HTTP GET 请求一个文件。

服务器处理请求,返回文件内容以及相应的 Header,其中包括 Etag(例如:627-4d648041f6b80)(假设服务器支持 Etag 生成并已开启了 Etag)状态码为 200。

第二次请求(断点续传):

客户端发起 HTTP GET 请求一个文件,同时发送 If-Range(该头的内容就是第一次请求时服务器返回的 Etag:627-4d648041f6b80)。

服务器判断接收到的 Etag 和计算出来的 Etag 是否匹配,如果匹配,返回range返回的内容,响应的状态码为 206;否则,状态码为 200。

PHP实现1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36$filename = '1.txt';

$file = fopen($filename, 'r');

$output = fopen('php://stdout', 'w');

$size = filesize($filename);

if (isset($_SERVER['HTTP_RANGE'])) {

// 断点续传,获取续传的开头和结尾

$range = str_replace('bytes=', '', $_SERVER['HTTP_RANGE']);

list($start, $end) = explode('-', $range);

$start = empty($start) ? 0 : $start;

$end = empty($end) ? ($size - 1) : $end;

$length = $end - $start + 1;

if ($length >= 0) {

// 返回断点续传code及相关头信息

header('HTTP/1.1 206 Partial Content');

header('Accept-Ranges: bytes');

header('Last-Modified: '.gmdate('D, d M Y 01:01:01', filemtime($filename)).' GMT');

header('Etags: ' . fileinode($filename));

header('Content-Length:' . $length);

header('Content-Range: bytes ' . $length . '/' . $size);

// 偏移文件指针读取固定长度返回

fseek($file, $start);

echo fread($file, $length);

}

} else {

// 非断点续传

header('cache-control:public');

header('content-type:application/octet-stream');

header('Content-Length: ' . $size);

header('Etags: ' . fileinode($filename));

header('Last-Modified: '.gmdate('D, d M Y 01:01:01', filemtime($filename)).' GMT');

header('content-disposition:attachment; filename=' . $filename);

echo fread($file, $size);

}

fclose($file);

fclose($output);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 Xdebug 进行代码调试,可以帮助我们快速定位程序中的问题,提高开发效率。下面是在 Yii 框架中使用 Xdebug 进行断点调试的步骤: 1. 安装 Xdebug 扩展 在 php.ini 中添加以下配置: ``` [xdebug] zend_extension="/path/to/xdebug.so" xdebug.remote_enable=1 xdebug.remote_host="localhost" xdebug.remote_port=9000 ``` 其中,`/path/to/xdebug.so` 是 Xdebug 扩展的文件路径,可以根据实际情况自行修改。`xdebug.remote_host` 和 `xdebug.remote_port` 是调试客户端的 IP 地址和端口,可以根据实际情况自行修改。 2. 配置 PHPStorm 在 PHPStorm 中,打开 `Settings -> Languages & Frameworks -> PHP -> Debug`,勾选`Allow parallel run`和 `Force break at first line when a script is outside the project`。 然后,在 `Settings -> Languages & Frameworks -> PHP -> Servers` 中,添加一个服务器,设置 `Host` 和 `Port`,并指定 `Debugger` 为 Xdebug。 最后,在 `Settings -> Languages & Frameworks -> PHP -> Debug -> DBGp Proxy` 中,设置 `IDE key` 为 `PHPSTORM`(或者其他自定义的值)。 3. 设置断点 在需要调试的代码行上,设置断点。然后,在浏览器中访问该页面,PHPStorm 会自动打开调试界面,并在断点处停止程序执行。 4. 进行调试 在调试界面中,可以查看变量的值、调用栈等信息,也可以逐行执行代码,查看程序执行过程。 以上就是在 Yii 框架中使用 Xdebug 进行断点调试的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值