基本原理与协议要点
HTTP断点续传的工作原理
HTTP断点续传依赖于 Range 请求头和服务器返回的 206 Partial Content 状态码,允许客户端请求资源的特定字节范围。
服务器需要在响应中包含 Accept-Ranges、Content-Range 或者 Content-Length 等信息,以便客户端判断是否支持断点续传以及当前可用的字节范围。
在实际应用中,常见模式是记录已下载的字节数,并在后续请求中通过 Range: bytes=start-end 指定继续下载的起止位置。
如果资源具备校验标记(如 ETag),还可以结合 If-Range 来实现无损断点续传,从而避免重复下载已变更的部分。
Java端实现总览
关键类与架构设计
在 Java 端实现 HTTP 断点续传,核心要点是计算已下载字节、构造正确的 Range 请求,以及使用 RandomAccessFile 以偏移位置写入数据。
常见的实现方案有两种主流客户端库:HttpClient(Java 11+ 原生库) 与 OkHttp。它们都支持自定义请求头并能逐步读取响应体。
设计上,可以用一个简单的下载任务对象来持续跟踪目标 URL、目标文件、已下载字节数,以及可能的 ETag、If-Range 缓存标识。
从零到一的实战代码:使用HttpClient实现断点续传
HttpClient实现断点续传的步骤与要点
步骤概述:首先读取本地已下载字节数,设置 Range 请求头,发送请求,处理 206 响应,使用 RandomAccessFile 在正确偏移处写入数据。
以下代码展示使用 HttpClient 的核心实现片段,包含如何打开偏移写入以及如何处理网络错误和重试。
// Java 11+ HttpClient 断点续传示例
import java.io.*;
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;public class HttpClientResumeDownloader {private final HttpClient client = HttpClient.newBuilder().build();public void download(String url, Path target) throws IOException, InterruptedException {long downloaded = 0;if (Files.exists(target)) {downloaded = Files.size(target);}HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).header("Range", "bytes=" + downloaded + "-").GET().build();HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());// 206 Partial Content 期望if (response.statusCode() == 206) {try (InputStream in = response.body();RandomAccessFile raf = new RandomAccessFile(target.toFile(), "rw")) {raf.seek(downloaded);byte[] buffer = new byte[8192];int len;while ((len = in.read(buffer)) != -1) {raf.write(buffer, 0, len);downloaded += len;}}} else if (response.statusCode() == 200) {// 服务器不支持 Range,需从头下载try (InputStream in = response.body();RandomAccessFile raf = new RandomAccessFile(target.toFile(), "rw")) {raf.setLength(0);byte[] buffer = new byte[8192];int len;while ((len = in.read(buffer)) != -1) {raf.write(buffer, 0, len);}}} else {throw new IOException("Unexpected response: " + response.statusCode());}}
}
从零到一的实战代码:使用OkHttp实现断点续传
OkHttp在断点续传中的应用
OkHttp 提供了更友好的 API,适合在 Android 或服务端环境中使用。关键点与 HttpClient 类似:读取本地偏移、发送 Range 请求、边写边下载。
下面的示例展示如何用 OkHttp 发起 Range 请求并写入本地文件。
// OkHttp 断点续传示例
import okhttp3.*;import java.io.*;
import java.nio.file.*;public class OkHttpResumeDownloader {private final OkHttpClient client = new OkHttpClient();public void download(String url, Path target) throws IOException {long downloaded = Files.exists(target) ? Files.size(target) : 0;Request request = new Request.Builder().url(url).addHeader("Range", "bytes=" + downloaded + "-").build();try (Response resp = client.newCall(request).execute()) {if (resp.isSuccessful()) {InputStream in = resp.body().byteStream();RandomAccessFile raf = new RandomAccessFile(target.toFile(), "rw");raf.seek(downloaded);byte[] buffer = new byte[8192];int read;while ((read = in.read(buffer)) != -1) {raf.write(buffer, 0, read);downloaded += read;}raf.close();} else if (resp.code() == 200) {// 不支持 Range,重新从头下载try (InputStream in = resp.body().byteStream();RandomAccessFile raf = new RandomAccessFile(target.toFile(), "rw")) {raf.setLength(0);byte[] buffer = new byte[8192];int len;while ((len = in.read(buffer)) != -1) {raf.write(buffer, 0, len);}}} else {throw new IOException("Unexpected code " + resp.code());}}}
}
边界情况、错误处理与兼容性
断点续传的鲁棒性设计
不是所有服务器都支持 Range 请求,因此需要在请求前后检查 Accept-Ranges 是否包含 bytes。

如果返回状态码 416(Range Not Satisfiable),通常意味着下载已完成或范围不合法,需要清理本地偏移并重新开始。
在需要对比资源完整性时,可以结合 ETag 与 If-Range 来避免错下载,确保版本的一致性。
下面的代码给出一个简单的 Content-Range 解析 的思路,帮助你理解服务端返回的边界信息。
// 简易 Content-Range 解析示例
// 形如: Content-Range: bytes 200-1000/5000
import java.util.regex.*;public class ContentRangeParser {public static long parseTotal(String contentRange) {// 仅做演示,真实解析需做更多边界校验if (contentRange == null) return -1;Matcher m = Pattern.compile("bytes (\\\\d+)-(\\\\d+)/(\\\\d+)").matcher(contentRange);if (m.find()) {return Long.parseLong(m.group(3));}return -1;}
}
性能优化与安全性考量
性能与风险点
使用断点续传可以提升大文件的下载鲁棒性,但需要评估网络波动对下载的影响,合理设置缓冲区和线程模型。
推荐在单线程下载的基础上,结合唯一下载锁和断点数据的持久化存储,避免因异常中断造成数据损坏。
在传输层,优先使用 TLS 来保护 Range 请求的字节段,注意对服务器证书、SNI 和 ALPN 的兼容性问题。


