1. 概述
本教程将提供如何 使用OkHttp 库下载二进制文件 的实际示例。
2.Maven依赖
我们首先添加基础库okhttp依赖项:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>
那么, 如果我们想为OkHttp库实现的模块编写集成测试,我们可以使用mockwebserver库 。该库具有模拟服务器及其响应的工具:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>4.9.1</version>
<scope>test</scope>
</dependency>
3. 请求二进制文件
我们将首先实现一个类,该类接收下载文件的 URL 作为参数 ,并为该 URL 创建并执行 HTTP 请求 。
为了使该类可测试,我们将在构造函数中 注入 OkHttpClient 和 writer :
public class BinaryFileDownloader implements AutoCloseable {
private final OkHttpClient client;
private final BinaryFileWriter writer;
public BinaryFileDownloader(OkHttpClient client, BinaryFileWriter writer) {
this.client = client;
this.writer = writer;
}
}
接下来,我们将实现 从 URL 下载文件 的方法:
public long download(String url) throws IOException {
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
ResponseBody responseBody = response.body();
if (responseBody == null) {
throw new IllegalStateException("Response doesn't contain a file");
}
double length = Double.parseDouble(Objects.requireNonNull(response.header(CONTENT_LENGTH, "1")));
return writer.write(responseBody.byteStream(), length);
}
下载文件的过程有四个步骤。使用 URL 创建请求。执行请求并接收响应。获取响应的正文,如果为 空则失败。 将响应正文的 字节 写入文件。
4. 将响应写入本地文件
要将从响应中接收到的 字节 写入本地文件,我们将实现一个 BinaryFileWriter 类,该类 将 InputStream 和 OutputStream 作为输入,并将内容从 InputStream 复制到 OutputStream 。
OutputStream 将被注入到构造函数中,以便该类可以测试:
public class BinaryFileWriter implements AutoCloseable {
private final OutputStream outputStream;
public BinaryFileWriter(OutputStream outputStream) {
this.outputStream = outputStream;
}
}
我们现在将实现将内容从 InputStream 复制到 OutputStream 的方法。该方法首先用 BufferedInputStream 包装 InputStream ,以便我们可以一次读取更多 字节 。然后我们准备一个数据缓冲区,在其中临时存储来自 InputStream 的 字节 。
最后,我们将缓冲的数据 写入 OutputStream 。只要 InputStream 有数据要读取,我们就这样做:
public long write(InputStream inputStream) throws IOException {
try (BufferedInputStream input = new BufferedInputStream(inputStream)) {
byte[] dataBuffer = new byte[CHUNK_SIZE];
int readBytes;
long totalBytes = 0;
while ((readBytes = input.read(dataBuffer)) != -1) {
totalBytes += readBytes;
outputStream.write(dataBuffer, 0, readBytes);
}
return totalBytes;
}
}
5. 获取文件下载进度
在某些情况下,我们可能想告诉用户文件下载的进度。
我们首先需要创建一个功能接口:
public interface ProgressCallback {
void onProgress(double progress);
}
然后,我们将在 BinaryFileWriter 类中使用它。这将为我们提供每一步下载器迄今为止写入的总 字节数 。
首先,我们将 ProgressCallback 作为字段添加到 writer 类中 。然后,我们将更新 write 方法以 接收响应的 长度 作为参数 。这将帮助我们计算进度。
然后,我们将 调用 onProgress 方法,并 根据迄今为止写入的 总字节数 和 长度计算出进度:
public class BinaryFileWriter implements AutoCloseable {
private final ProgressCallback progressCallback;
public long write(InputStream inputStream, double length) {
//...
progressCallback.onProgress(totalBytes / length * 100.0);
}
}
最后,我们将更新 BinaryFileDownloader 类以 使用总响应长度调用 write 方法 。我们将从 Content-Length 标头获取响应长度,然后将其传递给 write 方法:
public class BinaryFileDownloader {
public long download(String url) {
double length = getResponseLength(response);
return write(responseBody, length);
}
private double getResponseLength(Response response) {
return Double.parseDouble(Objects.requireNonNull(response.header(CONTENT_LENGTH, "1")));
}
}
六,结论
在本文中,我们实现了一个简单但实用的示例, 使用 OkHttp 库从 URL 下载二进制文件 。
要完整实现文件下载应用程序以及单元测试,请在 GitHub 上查看该项目。