实现思路
- 断点续传是Http协议自带的功能,通用的文件下载服务,都支持这个特性
- 比如Tomcat服务器,NodeJs服务器,SpringBoot内置的FileSystemResource,都支持该特性,实现起来一般几行代码即可
- 断点续传的工作原理大致如下
- 首先,客户端获取文件总长度,客户端在请求头中设置Accept-Encoding=identity
- 然后服务端会在回复头中返回Content-Length,即文件总长度
- 客户端在下载文件时,可以在请求头中设置range=bytes=start-end,指定只下载start-end部分的文件字节
- 这样客户端就能分多次下载,每次只下载一部分
- 带有range参数的请求,成功返回的ResponseCode是206,而不是200,表示局部下载
大致代码
以下代码中包含了很多工具类库,是无法直接使用的
主要是展示断点续传的核心思路,大家将其中的工具和请求库,替换成自己的代码即可
public static void main(String[] args) {
DownloadConfig config = new DownloadConfig();
config.url = "http://192.168.8.124:18001/common/file/download/bdce9ddd7cda4e81bdf7e80d49f0c636.apk";
config.name = TextUtil.random();
config.ext = ".apk";
config.file();
FileDownloader.start(config);
}
public class DownloadConfig {
public String url;
public String name;
public String ext;
public Long total;
public Long finish;
public String percent = "0%";
public String file;
public boolean finished() {
return total != null && finish != null && finish.intValue() == total.intValue();
}
public void file() {
file = "C:/" + name + ext;
FileUtil.createFile(file);
}
}
public class FileDownloader {
//开始下载
public static void start(DownloadConfig config) {
while (true) {
if (config.finished()) {
Threads.sleep(1000);
break;
}
if (config.total == null)
obtainFileLength(config);
else
downloadFilePart(config);
Threads.sleep(100);
}
}
//获取文件长度
protected static void obtainFileLength(DownloadConfig config) {
Postman.create()
.url(config.url)
.method(HttpMethod.GET)
.head("Accept-Encoding", "identity")
.onException((postman, call, e) -> {
Console.error("PackageDownloader obtainFileLength fail");
})
.onResponse((postman, call, response) -> {
String length = response.headers().get("Content-Length");
config.total = Long.valueOf(length);
config.finish = 0L;
config.percent = "0%";
}).executeInBlocking();
}
//下载文件片段
protected static void downloadFilePart(DownloadConfig config) {
final long start = config.finish;
final long end = start + 10 * 1024 * 1024 > config.total - 1 ? config.total - 1 : start + 10 * 1024 * 1024;
Postman.create()
.url(config.url)
.method(HttpMethod.GET)
.head("range", "bytes=" + start + "-" + end)
.onException((postman, call, e) -> {
Console.error("PackageDownloader obtainFileLength fail");
})
.onResponse((postman, call, response) -> {
byte[] bytes = response.body().bytes();
FileUtil.randomWrite(config.file, bytes, config.total, start);
config.finish = end + 1;
config.percent = config.finish / (double) config.total + "";
if (config.finished())
config.percent = "100%";
}).executeInBlocking();
}
}