diff --git a/core-java-io/pom.xml b/core-java-io/pom.xml
index 1437b85ac2..21e931656d 100644
--- a/core-java-io/pom.xml
+++ b/core-java-io/pom.xml
@@ -196,6 +196,12 @@
${hsqldb.version}
runtime
+
+
+ org.asynchttpclient
+ async-http-client
+ ${async-http-client.version}
+
@@ -365,6 +371,7 @@
2.4.0
2.1.0.1
1.19
+ 2.4.5
\ No newline at end of file
diff --git a/core-java-io/src/main/java/com/baeldung/download/FileDownload.java b/core-java-io/src/main/java/com/baeldung/download/FileDownload.java
new file mode 100644
index 0000000000..c7dd154023
--- /dev/null
+++ b/core-java-io/src/main/java/com/baeldung/download/FileDownload.java
@@ -0,0 +1,89 @@
+package com.baeldung.download;
+
+import org.apache.commons.io.FileUtils;
+import org.asynchttpclient.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.concurrent.ExecutionException;
+
+public class FileDownload {
+
+ public static void downloadWithJavaIO(String url, String localFilename) {
+
+ try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(localFilename)) {
+
+ byte dataBuffer[] = new byte[1024];
+ int bytesRead;
+ while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
+ fileOutputStream.write(dataBuffer, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void downloadWithJava7IO(String url, String localFilename) {
+
+ try (InputStream in = new URL(url).openStream()) {
+ Files.copy(in, Paths.get(localFilename), StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void downloadWithJavaNIO(String fileURL, String localFilename) throws IOException {
+
+ URL url = new URL(fileURL);
+ try (ReadableByteChannel readableByteChannel = Channels.newChannel(url.openStream());
+ FileOutputStream fileOutputStream = new FileOutputStream(localFilename); FileChannel fileChannel = fileOutputStream.getChannel()) {
+
+ fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
+ }
+ }
+
+ public static void downloadWithApacheCommons(String url, String localFilename) {
+
+ int CONNECT_TIMEOUT = 10000;
+ int READ_TIMEOUT = 10000;
+ try {
+ FileUtils.copyURLToFile(new URL(url), new File(localFilename), CONNECT_TIMEOUT, READ_TIMEOUT);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ public static void downloadWithAHC(String url, String localFilename) throws ExecutionException, InterruptedException, IOException {
+
+ FileOutputStream stream = new FileOutputStream(localFilename);
+ AsyncHttpClient client = Dsl.asyncHttpClient();
+
+ client.prepareGet(url)
+ .execute(new AsyncCompletionHandler() {
+
+ @Override
+ public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
+ stream.getChannel()
+ .write(bodyPart.getBodyByteBuffer());
+ return State.CONTINUE;
+ }
+
+ @Override
+ public FileOutputStream onCompleted(Response response) throws Exception {
+ return stream;
+ }
+ })
+ .get();
+
+ stream.getChannel().close();
+ client.close();
+ }
+
+}
diff --git a/core-java-io/src/main/java/com/baeldung/download/ResumableDownload.java b/core-java-io/src/main/java/com/baeldung/download/ResumableDownload.java
new file mode 100644
index 0000000000..55ff485e40
--- /dev/null
+++ b/core-java-io/src/main/java/com/baeldung/download/ResumableDownload.java
@@ -0,0 +1,62 @@
+package com.baeldung.download;
+
+import java.io.*;
+import java.net.*;
+
+public class ResumableDownload {
+
+ public static long downloadFile(String downloadUrl, String saveAsFileName) throws IOException, URISyntaxException {
+
+ File outputFile = new File(saveAsFileName);
+ URLConnection downloadFileConnection = new URI(downloadUrl).toURL()
+ .openConnection();
+ return transferDataAndGetBytesDownloaded(downloadFileConnection, outputFile);
+ }
+
+ private static long transferDataAndGetBytesDownloaded(URLConnection downloadFileConnection, File outputFile) throws IOException {
+
+ long bytesDownloaded = 0;
+ try (InputStream is = downloadFileConnection.getInputStream(); OutputStream os = new FileOutputStream(outputFile, true)) {
+
+ byte[] buffer = new byte[1024];
+
+ int bytesCount;
+ while ((bytesCount = is.read(buffer)) > 0) {
+ os.write(buffer, 0, bytesCount);
+ bytesDownloaded += bytesCount;
+ }
+ }
+ return bytesDownloaded;
+ }
+
+ public static long downloadFileWithResume(String downloadUrl, String saveAsFileName) throws IOException, URISyntaxException {
+ File outputFile = new File(saveAsFileName);
+
+ URLConnection downloadFileConnection = addFileResumeFunctionality(downloadUrl, outputFile);
+ return transferDataAndGetBytesDownloaded(downloadFileConnection, outputFile);
+ }
+
+ private static URLConnection addFileResumeFunctionality(String downloadUrl, File outputFile) throws IOException, URISyntaxException, ProtocolException, ProtocolException {
+ long existingFileSize = 0L;
+ URLConnection downloadFileConnection = new URI(downloadUrl).toURL()
+ .openConnection();
+
+ if (outputFile.exists() && downloadFileConnection instanceof HttpURLConnection) {
+ HttpURLConnection httpFileConnection = (HttpURLConnection) downloadFileConnection;
+
+ HttpURLConnection tmpFileConn = (HttpURLConnection) new URI(downloadUrl).toURL()
+ .openConnection();
+ tmpFileConn.setRequestMethod("HEAD");
+ long fileLength = tmpFileConn.getContentLengthLong();
+ existingFileSize = outputFile.length();
+
+ if (existingFileSize < fileLength) {
+ httpFileConnection.setRequestProperty("Range", "bytes=" + existingFileSize + "-" + fileLength);
+ } else {
+ throw new IOException("File Download already completed.");
+ }
+ }
+ return downloadFileConnection;
+ }
+
+}
diff --git a/core-java-io/src/test/java/com/baeldung/download/FileDownloadIntegrationTest.java b/core-java-io/src/test/java/com/baeldung/download/FileDownloadIntegrationTest.java
new file mode 100644
index 0000000000..81ac391958
--- /dev/null
+++ b/core-java-io/src/test/java/com/baeldung/download/FileDownloadIntegrationTest.java
@@ -0,0 +1,91 @@
+package com.baeldung.download;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.concurrent.ExecutionException;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FileDownloadIntegrationTest {
+
+ static String FILE_URL = "http://ovh.net/files/1Mio.dat";
+ static String FILE_NAME = "file.dat";
+ static String FILE_MD5_HASH = "6cb91af4ed4c60c11613b75cd1fc6116";
+
+ @Test
+ public void givenJavaIO_whenDownloadingFile_thenDownloadShouldBeCorrect() throws NoSuchAlgorithmException, IOException {
+
+ FileDownload.downloadWithJavaIO(FILE_URL, FILE_NAME);
+ assertTrue(checkMd5Hash(FILE_NAME));
+ }
+
+ @Test
+ public void givenJavaNIO_whenDownloadingFile_thenDownloadShouldBeCorrect() throws NoSuchAlgorithmException, IOException {
+
+ FileDownload.downloadWithJavaNIO(FILE_URL, FILE_NAME);
+ assertTrue(checkMd5Hash(FILE_NAME));
+ }
+
+ @Test
+ public void givenJava7IO_whenDownloadingFile_thenDownloadShouldBeCorrect() throws NoSuchAlgorithmException, IOException {
+
+ FileDownload.downloadWithJava7IO(FILE_URL, FILE_NAME);
+ assertTrue(checkMd5Hash(FILE_NAME));
+ }
+
+ @Test
+ public void givenAHCLibrary_whenDownloadingFile_thenDownloadShouldBeCorrect() throws NoSuchAlgorithmException, IOException, ExecutionException, InterruptedException {
+
+ FileDownload.downloadWithAHC(FILE_URL, FILE_NAME);
+ assertTrue(checkMd5Hash(FILE_NAME));
+ }
+
+ @Test
+ public void givenApacheCommonsIO_whenDownloadingFile_thenDownloadShouldBeCorrect() throws NoSuchAlgorithmException, IOException {
+
+ FileDownload.downloadWithApacheCommons(FILE_URL, FILE_NAME);
+ assertTrue(checkMd5Hash(FILE_NAME));
+ }
+
+ @Test
+ public void givenJavaIO_whenDownloadingFileStops_thenDownloadShouldBeResumedCorrectly() throws NoSuchAlgorithmException, IOException, URISyntaxException {
+
+ ResumableDownload.downloadFileWithResume(FILE_URL, FILE_NAME);
+ assertTrue(checkMd5Hash(FILE_NAME));
+ }
+
+ private boolean checkMd5Hash(String filename) throws IOException, NoSuchAlgorithmException {
+
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ md.update(Files.readAllBytes(Paths.get(filename)));
+ byte[] digest = md.digest();
+ String myChecksum = DatatypeConverter.printHexBinary(digest);
+
+ return myChecksum.equalsIgnoreCase(FILE_MD5_HASH);
+ }
+
+ @BeforeClass
+ public static void setup() throws IOException {
+ if (Files.exists(Paths.get(FILE_NAME))) {
+ Files.delete(Paths.get(FILE_NAME));
+ }
+ }
+
+ @After
+ public void cleanup() throws IOException {
+ if (Files.exists(Paths.get(FILE_NAME))) {
+ Files.delete(Paths.get(FILE_NAME));
+ }
+ }
+}