BAEL-4856 Download a binary file using OkHttp

This commit is contained in:
Iulian Manda 2021-05-31 15:51:38 +03:00
parent a85936e769
commit 9cba6e4c07
5 changed files with 249 additions and 0 deletions

View File

@ -0,0 +1,26 @@
package com.baeldung.okhttp.download;
import okhttp3.OkHttpClient;
import java.io.FileOutputStream;
public class BinaryFileDownloadApplication {
public static void main(String[] args) {
String url = args[0];
String file = args[1];
try (BinaryFileDownloader downloader = new BinaryFileDownloader(new OkHttpClient(),
new BinaryFileWriter(new FileOutputStream(file)))) {
long downloadSize = downloader.download(url);
double downloadSizeInMB = convertToMB(downloadSize);
System.out.printf("Successfully downloaded file %s from %s. Total size %.2fMB%n", file, url, downloadSizeInMB);
} catch (Exception ex) {
System.err.printf("Could not download file %s from %s. %nError: %s%n", file, url, ex);
}
}
private static double convertToMB(double downloadSize) {
return downloadSize / (1024.0 * 1024.0);
}
}

View File

@ -0,0 +1,54 @@
package com.baeldung.okhttp.download;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
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;
}
public long download(String url) throws IOException {
Request request = createRequest(url);
Response response = executeRequest(request);
ResponseBody responseBody = getResponseBodyOrFail(response);
return write(responseBody);
}
@NotNull
private Request createRequest(String url) {
return new Request.Builder().url(url).build();
}
@NotNull
private Response executeRequest(Request request) throws IOException {
return client.newCall(request).execute();
}
private ResponseBody getResponseBodyOrFail(Response response) {
ResponseBody responseBody = response.body();
if (responseBody == null) {
throw new IllegalStateException("Response doesn't contain a file");
}
return responseBody;
}
private long write(ResponseBody responseBody) throws IOException {
return writer.write(responseBody.byteStream());
}
@Override
public void close() throws Exception {
writer.close();
}
}

View File

@ -0,0 +1,34 @@
package com.baeldung.okhttp.download;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class BinaryFileWriter implements AutoCloseable {
private static final int CHUNK_SIZE = 1024;
private final OutputStream outputStream;
public BinaryFileWriter(OutputStream outputStream) {
this.outputStream = outputStream;
}
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;
}
}
@Override
public void close() throws IOException {
outputStream.close();
}
}

View File

@ -0,0 +1,80 @@
package com.baeldung.okhttp.download;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.InputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class BinaryFileDownloaderTest {
@Mock
private OkHttpClient client;
@Mock
private BinaryFileWriter writer;
@InjectMocks
private BinaryFileDownloader tested;
@Test
public void givenUrlAndResponse_whenDownload_thenExpectFileWritten() throws Exception {
String url = "http://example.com/file";
Call call = mock(Call.class);
when(client.newCall(any(Request.class))).thenReturn(call);
ResponseBody body = ResponseBody.create("BODY", MediaType.get("application/text"));
Response response = createResponse(url, body);
when(call.execute()).thenReturn(response);
when(writer.write(any())).thenReturn(1L);
try (BinaryFileDownloader tested = new BinaryFileDownloader(client, writer)) {
long size = tested.download(url);
assertEquals(1L, size);
verify(writer).write(any(InputStream.class));
}
verify(writer).close();
}
@Test
public void givenUrlAndResponseWithNullBody_whenDownload_thenExpectIllegalStateException() throws Exception {
String url = "http://example.com/file";
Call call = mock(Call.class);
when(client.newCall(any(Request.class))).thenReturn(call);
Response response = createResponse(url, null);
when(call.execute()).thenReturn(response);
assertThrows(IllegalStateException.class, () -> tested.download(url));
verify(writer, times(0)).write(any(InputStream.class));
}
@NotNull
private Response createResponse(String url, ResponseBody body) {
Request request = new Request.Builder().url(url).build();
return new Response.Builder()
.code(200)
.request(request)
.protocol(Protocol.HTTP_2)
.message("Message")
.body(body)
.build();
}
}

View File

@ -0,0 +1,55 @@
package com.baeldung.okhttp.download;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.InputStream;
import java.io.OutputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class BinaryFileWriterTest {
@Mock
private OutputStream outputStream;
@Test
public void givenInputStream_whenWrite_thenExpectWritten() throws Exception {
InputStream inputStream = mock(InputStream.class);
when(inputStream.read(any(), anyInt(), anyInt())).thenReturn(10, -1);
try (BinaryFileWriter tested = new BinaryFileWriter(outputStream)) {
long result = tested.write(inputStream);
assertEquals(10, result);
verify(outputStream).write(any(), eq(0), eq(10));
verify(inputStream).close();
}
verify(outputStream).close();
}
@Test
public void givenInputStreamEmpty_whenWrite_thenExpectNotWritten() throws Exception {
InputStream inputStream = mock(InputStream.class);
try (BinaryFileWriter tested = new BinaryFileWriter(outputStream)) {
long result = tested.write(inputStream);
assertEquals(0, result);
verify(outputStream, times(0)).write(any(), anyInt(), anyInt());
verify(inputStream).close();
}
verify(outputStream).close();
}
}