Перейти к основному содержимому

Загрузите двоичный файл с помощью OkHttp

· 4 мин. чтения

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-адрес, откуда можно загрузить файл, а также создает и выполняет HTTP-запрос для этого URL-адреса.

Чтобы сделать класс тестируемым, мы внедрим OkHttpClient и модуль записи в конструктор:

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 . Метод сначала оборачивает InputStream в BufferedInputStream , чтобы мы могли считывать больше байтов за раз. Затем мы готовим буфер данных, в котором мы временно храним байты из 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 в качестве поля в класс записи . Затем мы обновим метод записи , чтобы получить в качестве параметра длину ответа . Это поможет нам рассчитать прогресс.

Затем мы вызовем метод onProgress с вычисленным прогрессом из общего количества записанных байтов и длины:

public class BinaryFileWriter implements AutoCloseable {
private final ProgressCallback progressCallback;
public long write(InputStream inputStream, double length) {
//...
progressCallback.onProgress(totalBytes / length * 100.0);
}
}

Наконец, мы обновим класс BinaryFileDownloader , чтобы он вызывал метод записи с общей длиной ответа . Мы получим длину ответа из заголовка Content-Length , а затем передадим ее методу записи :

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")));
}
}

6. Заключение

В этой статье мы реализовали простой, но практичный пример загрузки бинарного файла с URL с помощью библиотеки OkHttp .

Для полной реализации приложения загрузки файлов, а также модульных тестов, ознакомьтесь с проектом на GitHub .