HTTP 断点续传笔记

现在互联网对断点续传功能支持已经十分普及,这种技术不仅让过去因为下载中断而只能从头再来的痛苦大大减少,同时发展了多线程下载让人们能够极限利用带宽节省网络传输时间。

虽然现在绝大多数服务器软件(IIS/nginx/apache等)都支持断点续传,但之前拿PHP写的一个下载的服务需要把请求转发到别处(说白了盗链),这时候自带断点续传就不能很好处理以上请求。后面是手动处理、实现断点续传的笔记。

断点续传是HTTP/1.1标准新增的功能,为此特别新定义了一个Header和HTTP状态

Request:

    Range: Bytes=([\d][-][\d][,])+

Response

    HTTP/1.1 206 Partial Content

当客户端发送请求时带上Range,服务器会返回状态206并按Range规则返回对应的部分数据。

这部分好做,得到请求后查找Header里的Range头,若存在则带上随转发请求一起发去资源服务,同时设置http状态码为206。

isset($_SERVER[‘HTTP_RANGE’]);
curl_setopt($ch, CURLOPT_RANGE, substr($_SERVER[‘HTTP_RANGE’], 6));
http_response_code(206);

然而在写完这些程序后拿IDM尝试多线程下载发现,虽然一开始提示支持断点续传,然而暂停再继续下载后会提示数据冲突然后重新下载。

抓包分析了一下发现每次返回的数据都比实际请求量多三个字节,例如请求Range: 0-100,返回的body-size却是103字节,再观察十六进制发现是头部多出了3个字节:EF BB BF,也就是通常说的UTF8-BOM。(什么是BOM请百度)

于是问题比较明确了,是PHP与UTF-8 BOM的问题

UTF-8 BOM作为文件标识总在文件头部占3字节
PHP文件起始标签‘<?’在BOM后
PHP默认会将php文件php标签外的内容原样输出(不讨论if (xxx){?>xxx<?}的情况),导致输出内容总会多出BOM头
实际上一般情况(输出文本/HTML)下无需处理BOM,浏览器也会主动处理掉BOM
但对于需要输出二进制时(例如下载文件)特别是需要断点续传、控制range时必须去除文件BOM头。

处理办法:
Win下记事本另存为、npp编码等
Linux下用sed清除BOM文件:
    sed -i ‘1s/^\xef\xbb\xbf//’ 1.php
VIM下清除BOM指令:
    :set nobomb

BOM处理完,IDM也能正常多线程断点续传了,笔记告一段落

另外Chrome、FireFox在处理带UTF-8的URL时会自动执行URLencode,但IE不会
但如果使用JS生成连接时执行encodeURI,Chrome在解析连接时会强行把%再encode成%25……

HTTP Content-MD5

留下评论