Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
Java File Downloader, download File using HttpURLConnection of Java

### Features
1. If File already partially downloaded, download resume from there.
2. Provide CLI interface for Download File
3. CLI display progressbar on console for monitor downloading process.
1. Download file from HttpUrl
2. If File already partially downloaded, download resume from there.
3. Provide CLI interface for Download File
4. CLI display progressbar on console for monitor downloading process.

### How to use
1. Execute **gradle** command from home directory
Expand Down Expand Up @@ -37,4 +38,3 @@ Please enter Download File URL:http://speedtest.ftp.otenet.gr/files/test100k.db
Please enter File Location : /tmp
100 % [...................................................]
```

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.arkaya.filedownloader.constant.FileDownloadStatus;
import com.arkaya.filedownloader.download.constant.FileDownloadKeyConstant;
import com.arkaya.filedownloader.download.task.FileDownloadInfo;
import com.arkaya.filedownloader.download.task.FileDownloadTaskHandler;
import com.arkaya.filedownloader.request.http.HttpFileDownloadRequest;
import com.arkaya.filedownloader.response.FileDownloadResponse;
Expand All @@ -11,9 +12,12 @@
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;

import javax.annotation.PreDestroy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Component
public class FileDownloadCLIExecutor {
Expand All @@ -24,6 +28,17 @@ public class FileDownloadCLIExecutor {
@Autowired
private FileDownloadTaskHandler fileDownloadTaskHandler;

private final ExecutorService executorService;

public FileDownloadCLIExecutor() {
this.executorService = Executors.newSingleThreadExecutor();
}

@PreDestroy
public void destroy() {
this.executorService.shutdown();
}

public void execute() {
Scanner scanner = new Scanner(System.in);
System.out.print("Please enter Download File URL:");
Expand Down Expand Up @@ -53,9 +68,13 @@ public void execute() {
}

private void startProgressBar(HttpFileDownloadRequest httpFileDownloadRequest) {
FileDownloadProgressbar fileDownloadProgressbar = new FileDownloadProgressbar();
String partialFileName = httpFileDownloadRequest.getAdditionalProperty(FileDownloadKeyConstant.PARTIAL_FILE_NAME);
fileDownloadTaskHandler.getFileDownloadTask(partialFileName)
.ifPresent(fileDownloadInfo -> new FileDownloadProgressbar(fileDownloadInfo).startProgressBar());
.ifPresent((FileDownloadInfo fileDownloadInfo) -> {
fileDownloadInfo.registerFileDownloadListener(fileDownloadProgressbar);
executorService.submit(fileDownloadProgressbar);
});
}

private boolean isValidFileLocation(String fileLocation) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,68 +1,70 @@
package com.arkaya.filedownloader.cli;

import com.arkaya.filedownloader.constant.FileDownloadStatus;
import com.arkaya.filedownloader.download.constant.FileDownloadKeyConstant;
import com.arkaya.filedownloader.download.task.FileDownloadInfo;
import com.arkaya.filedownloader.request.http.HttpFileDownloadRequest;
import org.apache.commons.lang3.math.NumberUtils;
import com.arkaya.filedownloader.download.FileDownloadEvent;
import com.arkaya.filedownloader.download.FileDownloadListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Callable;

public class FileDownloadProgressbar {
public class FileDownloadProgressbar implements FileDownloadListener, Callable<FileDownloadStatus> {
private final static Logger LOGGER = LoggerFactory.getLogger(FileDownloadProgressbar.class);
private FileDownloadInfo fileDownloadInfo;
private final Path partialFileName;
private final long fileSize;
private FileDownloadStatus fileDownloadStatus;
private long fileSize;
private long partialFileSize;

public FileDownloadProgressbar(FileDownloadInfo fileDownloadInfo) {
HttpFileDownloadRequest fileDownloadRequest = fileDownloadInfo.getFileDownloadRequest();
this.partialFileName = Paths.get(fileDownloadRequest.getAdditionalProperty(FileDownloadKeyConstant.PARTIAL_FILE_NAME));
this.fileSize = NumberUtils.toLong(fileDownloadRequest.getAdditionalProperty(FileDownloadKeyConstant.FILE_SIZE));
this.fileDownloadInfo = fileDownloadInfo;
public FileDownloadProgressbar() {
this.fileDownloadStatus = FileDownloadStatus.INITIATE;
}

public void startProgressBar() {
@Override
public void onDownloadingFile(FileDownloadEvent fileDownloadEvent) {
this.fileDownloadStatus = fileDownloadEvent.getFileDownloadStatus();
this.fileSize = fileDownloadEvent.getFileSize();
this.partialFileSize = fileDownloadEvent.getPartialFileSize();
}

@Override
public FileDownloadStatus call() throws Exception {
try {
updateProgress(0);
while (true) {

if (fileDownloadInfo.getFileDownloadStatus() == FileDownloadStatus.COMPLETE) {
updateProgress(100);
break;
}

while (fileDownloadInfo.getFileDownloadStatus() == FileDownloadStatus.DOWNLOADING) {
updateProgress(calculateProcessPercentage());
Thread.sleep(500);
}

if (fileDownloadInfo.getFileDownloadStatus() == FileDownloadStatus.FAIL) {
System.err.println("File Downloading Fail, Please contact system admin");
break;
Thread.sleep(500);
switch (fileDownloadStatus) {
case INITIATE:
break;
case DOWNLOADING:
updateProgress(calculateProcessPercentage());
break;
case COMPLETE:
updateProgress(calculateProcessPercentage());
return fileDownloadStatus;
case FAIL:
System.err.println("File Downloading Fail, Please contact system admin");
return fileDownloadStatus;
}
}
} catch (Exception e) {
System.err.println("Error while downloading file, reason " + e.getMessage());
LOGGER.error("Error while downloading file", e);
e.printStackTrace();
System.err.println("Error while downloading file, Reason :" + e.getMessage());
}
return FileDownloadStatus.FAIL;
}

private int calculateProcessPercentage() throws IOException {
long partialFileSize = Files.size(partialFileName);
LOGGER.debug("calculate percentage for partial file size {} and file size {}", partialFileSize, fileSize);
return (int) Math.floor(100 * partialFileSize/fileSize);
return (int) Math.floor(100 * partialFileSize / fileSize);
}


static void updateProgress(int progressPercentage) {
StringBuilder builder = new StringBuilder("\r");
builder.append(progressPercentage +" % [");
builder.append(progressPercentage + " % [");
int i = 0;
for (; i <= progressPercentage/2; i++) {
for (; i <= progressPercentage / 2; i++) {
builder.append(".");
}
for (; i < 50; i++) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.arkaya.filedownloader.download;

import com.arkaya.filedownloader.constant.FileDownloadStatus;

/**
* Created by punit.patel on 1/9/17.
*/
public class FileDownloadEvent {
private long fileSize;
private long partialFileSize;
private FileDownloadStatus fileDownloadStatus;

public FileDownloadEvent(FileDownloadStatus fileDownloadStatus, long fileSize, long partialFileSize) {
this.fileDownloadStatus = fileDownloadStatus;
this.fileSize = fileSize;
this.partialFileSize = partialFileSize;
}

public long getFileSize() {
return fileSize;
}

public long getPartialFileSize() {
return partialFileSize;
}

public FileDownloadStatus getFileDownloadStatus() {
return fileDownloadStatus;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.arkaya.filedownloader.download;

public interface FileDownloadListener {
void onDownloadingFile(FileDownloadEvent fileDownloadEvent);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.arkaya.filedownloader.download.task;

import com.arkaya.filedownloader.constant.FileDownloadStatus;
import com.arkaya.filedownloader.download.FileDownloadListener;
import com.arkaya.filedownloader.request.http.HttpFileDownloadRequest;

public interface FileDownloadInfo {
HttpFileDownloadRequest getFileDownloadRequest();

FileDownloadStatus getFileDownloadStatus();

void registerFileDownloadListener(FileDownloadListener fileDownloadListener);
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
package com.arkaya.filedownloader.download.task;

import com.arkaya.filedownloader.constant.FileDownloadStatus;
import com.arkaya.filedownloader.download.FileDownloadListener;
import com.arkaya.filedownloader.request.http.HttpFileDownloadRequest;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class FileDownloadInfoDetail implements FileDownloadInfo {
private HttpFileDownloadRequest fileDownloadRequest;
private volatile FileDownloadStatus fileDownloadStatus;
private List<FileDownloadListener> fileDownloadListenerList;

public FileDownloadInfoDetail(HttpFileDownloadRequest fileDownloadRequest) {
this.fileDownloadRequest = fileDownloadRequest;
this.fileDownloadStatus = FileDownloadStatus.INITIATE;
this.fileDownloadListenerList = new CopyOnWriteArrayList<>();
}

@Override
public HttpFileDownloadRequest getFileDownloadRequest() {
return fileDownloadRequest;
}

@Override
public FileDownloadStatus getFileDownloadStatus() {
return fileDownloadStatus;
}

@Override
public void registerFileDownloadListener(FileDownloadListener fileDownloadListener) {
fileDownloadListenerList.add(fileDownloadListener);
}

public void changeFileDownloadStatus(FileDownloadStatus fileDownloadStatus) {
this.fileDownloadStatus = fileDownloadStatus;
}

public List<FileDownloadListener> getFileDownloadListenerList() {
return fileDownloadListenerList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import com.arkaya.filedownloader.constant.FileDownloadStatus;
import com.arkaya.filedownloader.download.FileDownloadEvent;
import com.arkaya.filedownloader.download.constant.FileDownloadKeyConstant;
import com.arkaya.filedownloader.download.http.HttpURLConnectionUtil;
import com.arkaya.filedownloader.download.util.FileDownloadUtil;
Expand All @@ -17,12 +18,10 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class FileDownloadTask implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(FileDownloadTask.class);
private static final int BUFFER_SIZE = 4896;
private static final int BUFFER_SIZE = 489600;
private final String partialFileName;
private final long fileSize;
private final String url;
Expand Down Expand Up @@ -52,6 +51,7 @@ public void run() {
Files.move(partialFilePath, downloadFilePath);
LOGGER.debug("File {} download completed", downloadFilePath);
changeStatus(FileDownloadStatus.COMPLETE);
notifyListeners(new FileDownloadEvent(FileDownloadStatus.COMPLETE, fileSize, partFileSize));
break;
}
long startByteRange = partFileSize;
Expand All @@ -61,17 +61,23 @@ public void run() {
}
partFileSize = endByteRange;
downloadFile(startByteRange, endByteRange);
notifyListeners(new FileDownloadEvent(FileDownloadStatus.DOWNLOADING, fileSize, partFileSize));
}
} catch (Exception e) {
LOGGER.error("Error while download File",e);
LOGGER.error("Error while download File", e);
changeStatus(FileDownloadStatus.FAIL);
notifyListeners(new FileDownloadEvent(FileDownloadStatus.FAIL, fileSize, 0));
}
}

private void changeStatus(FileDownloadStatus fileDownloadStatus) {
fileDownloadInfo.changeFileDownloadStatus(fileDownloadStatus);
}

private void notifyListeners(FileDownloadEvent fileDownloadEvent) {
fileDownloadInfo.getFileDownloadListenerList().forEach(fileDownloadListener -> fileDownloadListener.onDownloadingFile(fileDownloadEvent));
}

private void downloadFile(long startByteRange, long endByteRange) throws IOException {
LOGGER.debug("Download file from {} to {}", startByteRange, endByteRange);
HttpURLConnection connection = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,26 @@ public void startDownloading(HttpFileDownloadRequest httpFileDownloadRequest, Ht
executorService.execute(fileDownloadTask);
response.setFileDownloadStatus(FileDownloadStatus.INITIATE)
.setResponseMessage(FileDownloadStatus.INITIATE.getMessage());
postProcessor(httpFileDownloadRequest, fileDownloadInfoDetail);
}

private void postProcessor(HttpFileDownloadRequest httpFileDownloadRequest, FileDownloadInfoDetail fileDownloadInfoDetail) {
fileDownloadInfoDetail.registerFileDownloadListener(fileDownloadEvent -> {
switch (fileDownloadEvent.getFileDownloadStatus()) {
case COMPLETE:
remove(httpFileDownloadRequest);
break;
case FAIL:
remove(httpFileDownloadRequest);
break;
}
});
}

public FileDownloadInfo remove(HttpFileDownloadRequest httpFileDownloadRequest) {
String partialFileName = httpFileDownloadRequest.getAdditionalProperty(FileDownloadKeyConstant.PARTIAL_FILE_NAME);
return partialFileNameToTaskMap.remove(partialFileName);
}


}
5 changes: 4 additions & 1 deletion src/main/resources/logback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
<configuration>

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file> file-downloader.log </file>
<file>file-downloader.log </file>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d %m%n</Pattern>
</encoder>
</appender>

<logger name="ch.qos" level="WARN"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class FileDownloadTaskTest {

private FileDownloadTask fileDownloadTask;
private String fileLocation;

@Before
public void setUp() throws Exception {
this.fileLocation = File.separator + "tmp";
Expand Down