Hướng Dẫn Sử Dụng
通过代理下载文件:连接中断后使用 HTTP Range 续传

目录
通过代理下载文件时,连接可能在数据完整接收之前中断。如果下载工具总是从头开始,用户就必须重新传输已有的部分,从而延长下载时间并产生不必要的流量。
HTTP Range 允许请求资源的一部分,而非全部内容。不过,要安全地续传文件,需要确认服务器确实返回了正确的数据段,且源文件未发生变化。本文介绍如何在代理连接中断后进行检查、处理响应并组织续传流程。
HTTP Range 如何帮助续传文件?
在普通下载中,客户端会请求整个文件。如果连接中断,本地文件只包含已成功接收并写入的数据。客户端无需丢弃这部分数据,而是可以发送新请求,仅获取缺失的部分。
对于字节范围,Range 标头用于指定起始位置,也可以包含结束位置。续传至资源末尾时,请求通常只需指定起始字节。
该位置从文件开头计算,索引从零开始。如果本地文件已包含一段连续数据,则下一次请求的起始位置就是已保存数据段的大小,以字节为单位。
一个重要前提是,源文件在各次下载之间必须保持不变。如果内容已经变化,将旧版本的前半部分与新版本的后半部分拼接,会生成无效文件,即使最终大小看起来完整无缺。
代理如何影响续传能力?
代理是转发流量的中间组件。代理连接中断时,当前传输会话可能结束,但这并不意味着续传能力也随之丧失。客户端可以建立新连接,并请求缺失的数据部分。
这种能力取决于整条传输链路:提供文件的服务器、代理以及下载软件。服务器必须正确处理 Range 请求;代理必须正确转发;下载软件必须在追加写入之前检查响应。
区分转发代理与终止 TLS 的代理
对于通过常规 CONNECT 隧道建立的 HTTPS 连接,代理不会直接读取或修改 TLS 连接内部的 HTTP 标头。此时,对 Range 的支持主要取决于目标服务器和客户端。
如果代理终止 TLS 或处理 HTTP 内容,它就可能影响标头、缓存或资源表示。因此,应使用实际下载时采用的代理线路进行检查,而不应只测试直连。
如何检查服务器是否支持续传
使用 HEAD 请求检查标头
HEAD 请求允许查看标头,而不主动下载响应正文。使用 curl 时,可以采用以下命令模板,并将示例地址替换为实际信息:
curl --head --location --proxy http://proxy.example:PORT https://download.example/archive.zip
Accept-Ranges: bytes 标头表明服务器声明支持按字节检索。如果响应包含 Accept-Ranges: none,则表示服务器声明该资源不支持范围请求。
不过,缺少 Accept-Ranges 并不足以断定无法续传。有些服务器仍会处理 Range,只是不声明这种能力。HEAD 的处理方式也可能与 GET 不同,因此这只是初步检查。
发送小范围的 GET 请求
实际的检查方法是请求文件中极短的一段,并观察响应。下面的示例请求第一个字节,同时将标头和正文分别保存到两个文件中:
curl --location --proxy http://proxy.example:PORT --range 0-0 --dump-header range-headers.txt --output range-probe.bin https://download.example/archive.zip
服务器可能忽略 Range 并返回全部内容。因此,应在设置了适当时间或流量限制的测试环境中执行,同时监控响应,以便在必要时停止。
如果发生重定向,需要读取最终包含文件数据的响应标头。不应根据中间重定向响应的标头作出判断。
理解 HTTP Range 的重要响应
206 Partial Content:已返回部分内容
状态码 206 Partial Content 表示服务器返回了部分内容。对于单个范围的请求,Content-Range 标头会描述返回的字节段,并且可能提供资源的总大小。
追加写入之前,必须检查 Content-Range 中的起始位置是否与请求的续传位置一致。仅看到 206 状态,并不足以保证数据能够正确拼接。
200 OK:不得自动追加写入
如果发送了 Range 却收到 200 OK,响应可能包含整个资源。常见原因是服务器忽略了 Range,或者版本验证条件已不再满足。
不要将此响应正文直接追加到未完成的文件中。应停止并检查,或改为下载到一个新文件中。否则,文件可能包含重复的开头部分。
416 Range Not Satisfiable:需要重新核对状态
416 Range Not Satisfiable 响应表示无法满足所请求的范围。当起始位置超出当前资源的范围,或者本地文件已经包含全部数据时,都可能出现这种情况。
不应默认将其视为下载成功。如果 Content-Range 中提供了服务器声明的大小,需要检查该大小、核对版本,并验证完整性后再作判断。
代理连接中断后的文件续传流程
保存临时文件和版本信息
下载过程中,应使用临时文件名,避免其他应用程序误读尚未完成的文件。同时,保存 URL、ETag、Last-Modified,以及服务器提供的总大小。
ETag 有助于识别资源版本。与 If-Range 配合使用时,需要使用强 ETag;不要使用带有 W/ 前缀的弱 ETag。在符合 HTTP 相关条件的情况下,也可以使用修改时间,但如果存在强 ETag,通常不优先选择修改时间。
确定已成功写入的数据部分
连接中断后,应先关闭文件或将其同步到磁盘,再读取大小。续传位置必须以实际保存到磁盘上的字节数为依据,而不是程序已接收到内存中的字节数。
这种方法适用于顺序下载,即临时文件包含从资源开头开始的一段连续数据。如果采用多线程下载,文件大小并不能证明其中所有区域都已有数据;需要单独跟踪各个已完成的数据段。
发送 Range 并附带 If-Range 条件
在新请求中,Range 指定续传位置,而 If-Range 携带已下载版本的验证值。如果版本仍然匹配,服务器可以返回所请求的数据段。
如果版本已经变化,服务器可能返回整个资源,而不是缺失的部分。客户端必须识别这种响应,且不能将其与旧数据拼接。
仅在验证响应后追加写入
- 在开启追加写入模式之前,检查状态码和 Content-Range。
- 核对起始位置是否与已保存数据部分的大小一致。
- 检查版本信息和内容表示。
- 单独处理错误响应、登录页面或非文件内容。
- 再次中断时,根据实际数据重新计算续传位置。
使用 curl 通过代理续传文件
curl 支持通过 --continue-at - 选项,根据现有输出文件的大小进行续传。基本命令模板如下:
curl --location --proxy http://proxy.example:PORT --continue-at - --output archive.zip.part https://download.example/archive.zip
如果连接中断,可以使用同一个输出文件重新运行该命令。curl 会根据本地文件确定续传位置,并请求相应的数据部分。
不过,该命令本身并不能保证源文件未发生变化。对于重要数据,需要管理版本信息、If-Range 条件以及下载后的验证步骤。不应使用 shell 的追加重定向,在未经检查的情况下拼接 HTTP 响应。
如果代理要求身份验证,请使用适当的凭据管理机制。避免让密码出现在其他人可能读取的命令历史、日志或进程列表中。
常见的续传失败原因
URL 或登录会话过期
带签名的下载链接和登录会话可能在下载过程中到期。收到身份验证错误时,需要更新访问权限。如果必须获取新的 URL,请先确认它仍指向同一版本的资源,再继续下载。
内容表示发生变化
HTTP Range 作用于服务器发送的表示。如果不同下载过程中,内容采用了不同的压缩或转换方式,字节位置可能不再兼容。
应保持内容协商标头一致。对于需要续传的二进制文件,可以请求 Accept-Encoding: identity,并检查实际的 Content-Encoding。不要将解压后数据的大小用作压缩表示的续传位置。
更换代理导致响应变化
更换代理不一定会导致无法续传,但可能影响身份验证、服务器选择或接收到的内容。更换连接线路后,仍然必须检查版本和返回的范围。
使用文件前验证下载是否完成
文件大小完整,并不意味着内容一定正确。下载完成后,如果可信的发布来源提供了哈希值,请进行比对。对于带有数字签名的软件包,应按照发布方的说明验证签名。
ETag 不一定是内容哈希,不应默认将其用作校验和。只有完成适当的检查后,才能将临时文件重命名为正式文件名。
使用 HTTP Range 通过代理下载文件要取得良好效果,需要同时满足三个条件:准确确定本地已有的数据部分、服务器返回正确的范围,以及资源仍为同一版本。完整落实这些步骤,有助于在连接故障后续传文件,同时不牺牲数据完整性。
相关文章