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