Resume Http File Download in Java

Resume http file download in java

 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if(ISSUE_DOWNLOAD_STATUS.intValue()==ECMConstant.ECM_DOWNLOADING){
File file=new File(DESTINATION_PATH);
if(file.exists()){
downloaded = (int) file.length();
connection.setRequestProperty("Range", "bytes="+(file.length())+"-");
}
}else{
connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
}
connection.setDoInput(true);
connection.setDoOutput(true);
progressBar.setMax(connection.getContentLength());
in = new BufferedInputStream(connection.getInputStream());
fos=(downloaded==0)? new FileOutputStream(DESTINATION_PATH): new FileOutputStream(DESTINATION_PATH,true);
bout = new BufferedOutputStream(fos, 1024);
byte[] data = new byte[1024];
int x = 0;
while ((x = in.read(data, 0, 1024)) >= 0) {
bout.write(data, 0, x);
downloaded += x;
progressBar.setProgress(downloaded);
}

This is not my code, but it works.

Implementing resume for download files via internet

1- You are getting null for httpURLConnection because you try to invoke it before being initialized,

i.e, this line

httpURLConnection = (HttpURLConnection) url.openConnection();

should come before this line:

String lastModified = httpURLConnection.getHeaderField("Last-Modified");

2- you can set the header before calling connect() on httpURLConnection
so you need to set whatever you want, then connect(). this way you should not get the error (android Cannot set request property after connection is made)

3- The 206 is perfectly right, it's what you should expect when using Range and it means Partial Content Success and that's what you are doing, you are getting part of the content, if you are getting full content you would get 200.

so to sum this up, your code can look like this:
Note: follow the //*** to see changes required.

EDIT: it all came to this line

httpURLConnection.setRequestProperty("If-Range", lastModified);

the error get thrown when you set that Property,

Anyways, when you look at this, it's meaningless, you are asking if last-modified is equal to the value that you just got from the connection!,
if you want to do this you need to store lastModified in your system,
then compare it with the one you got from the URLConn, and compare it to your file length (already downloaded)
then proceed with full download or resume download.

find new code below:

public void run() {
myLastModified = getLastModified(mFile.getName()); // get last stored value for this file (use file name or other key)
int total =0;

final URL url;
HttpURLConnection httpURLConnection = null;
try {
try {
url = new URL(mUrl);

httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setDoInput(true);

httpURLConnection.setConnectTimeout(30000);
httpURLConnection.setReadTimeout(30000);
httpURLConnection.setRequestMethod("GET");

//*** new way to handle download process
total = httpURLConnection.getContentLength();
if(mFile.exists()){
if(mFile.length() == total){
//we are done, return.
return;
}else{
//file was not completly donwloaded, now check lastModified:
long lastModified = httpURLConnection.getLastModified();//this gets the header "Last-Modified" and convert to long
if (lastModified == myLastModified) { //myLastModified should be retrived on each download and stored locally on ur system
downloadedLength = mFile.length();
Log.e("downloadedLength ", downloadedLength + "");
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setDoInput(true);

httpURLConnection.setConnectTimeout(30000);
httpURLConnection.setReadTimeout(30000);
httpURLConnection.setRequestMethod("GET");

httpURLConnection.setRequestProperty("Range", "bytes=" + downloadedLength + "-"+ total); //add + total (TO)

//append mode
fileOutputStream = new FileOutputStream(mFile, true);
}else{
//file was modified after 1st uncompleted-download:
storeLastModified(lastModified, mFile.getName()); // use file name as key. can store in db or file ...

//don't set ant Range ... we want full download, with a fresh file
fileOutputStream = new FileOutputStream(mFile);
}//last mod IF

}//Length check
}else{
//file not exist at all, create new file, set no Range we want full download...
mFile.createNewFile();
fileOutputStream = new FileOutputStream(mFile);
}//file exists.

} catch (IOException e) {
e.printStackTrace();
}
final int responseCode;

try {
responseCode = httpURLConnection.getResponseCode();

} catch (IOException e) {
e.printStackTrace();
Log.e("ER UPDATE ", e.getMessage());
}

//*****
if (responseCode == 200 || responseCode == 206) {
try {
inputStream = httpURLConnection.getInputStream();
} catch (IOException e) {
e.printStackTrace();
Log.e("IOException ", e.getMessage());
}
final byte[] buffer = new byte[4 * 1024];
int length = -1;
int finished = 0;
long start = System.currentTimeMillis();
try {
while ((length = inputStream.read(buffer)) != -1) {
if (!isDownloading()) {
throw new CanceledException("canceled");
}
fileOutputStream.write(buffer, 0, length);
finished += length;
if (System.currentTimeMillis() - start > 1000) {
onDownloadProgressing(finished, total);
start = System.currentTimeMillis();
}
}
onDownloadCompleted();
} catch (IOException e) {
e.printStackTrace();
Log.e("ER UPDATE ", e.getMessage());
}
} else {
Log.e("responseCode ", responseCode + "");
}
} catch (DownloadException e) {
e.printStackTrace();
Log.e("ER UPDATE ", e.getMessage());
} catch (CanceledException e) {
e.printStackTrace();
Log.e("ER UPDATE ", e.getMessage());
}
}

how to resume an interrupted download

Try using a "Range" request header:

// Open connection to URL.
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// Specify what portion of file to download.
connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
// here "downloaded" is the data length already previously downloaded.

// Connect to server.
connection.connect();

Having done that, you can seek at a given point (just before the length of your download data, say X) and start writing the newly downloaded data there. Be sure to use the same value X for the range header.

Details about 14.35.2 Range Retrieval Requests

More details and source code can be found here

Implement pause/resume in file downloading

Okay problem fixed, here is my code for other users who wants to implement pause/resume:

        if (outputFileCache.exists())
{
connection.setAllowUserInteraction(true);
connection.setRequestProperty("Range", "bytes=" + outputFileCache.length() + "-");
}

connection.setConnectTimeout(14000);
connection.setReadTimeout(20000);
connection.connect();

if (connection.getResponseCode() / 100 != 2)
throw new Exception("Invalid response code!");
else
{
String connectionField = connection.getHeaderField("content-range");

if (connectionField != null)
{
String[] connectionRanges = connectionField.substring("bytes=".length()).split("-");
downloadedSize = Long.valueOf(connectionRanges[0]);
}

if (connectionField == null && outputFileCache.exists())
outputFileCache.delete();

fileLength = connection.getContentLength() + downloadedSize;
input = new BufferedInputStream(connection.getInputStream());
output = new RandomAccessFile(outputFileCache, "rw");
output.seek(downloadedSize);

byte data[] = new byte[1024];
int count = 0;
int __progress = 0;

while ((count = input.read(data, 0, 1024)) != -1
&& __progress != 100)
{
downloadedSize += count;
output.write(data, 0, count);
__progress = (int) ((downloadedSize * 100) / fileLength);
}

output.close();
input.close();
}

Java: resume Download in URLConnection

Try:

connection.setRequestProperty("Range", "bytes=" + fcheck.length() + "-");

Lowercase the range specifier per the spec. Also, if your partial file was 500 bytes, that means your byte range that you have is 0-499, and you want 500+.

JAVA - Resume/Pause https connection download

You need to use a separate thread using SwingUtilities. In the GUI thread you set a flag if you want to stop. If you want to resume, you can reopen the URL and use the skip method of the InputStream to start from where you left. To pause, resume you can use signals. See http://tutorials.jenkov.com/java-concurrency/thread-signaling.html.

You need to educate yourself about multi-threading.

how to resume an interrupted download - part 2

To resume a download, you need to send not only the Range request header, but also the If-Range request header which should contain either the unique file identifier or the file modification timestamp.

If the server returns an ETag response header on the initial download, then you should use it in the If-Range header of the subsequent resume requests. Or if it returns a Last-Modified response header, then you should use it in the If-Range request header instead.

Looking at your logs, the server has sent a Last-Modified response header. So you should send it back along in an If-Range header of the resume request.

// Initial download.
String lastModified = connection.getHeaderField("Last-Modified");

// ...

// Resume download.
connection.setRequestProperty("If-Range", lastModified);

The server will use this information to verify if you're requesting exactly the same file.



Related Topics



Leave a reply



Submit