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
A Simple Scenario Using Wait() and Notify() in Java
How to Ignore Ssl Certificate Errors in Apache Httpclient 4.0
Similarity String Comparison in Java
When Do Java Generics Require <? Extends T> Instead of <T> and Is There Any Downside of Switching
Is System.Nanotime() Completely Useless
Convert Iterable to Stream Using Java 8 Jdk
How to Compare Two Version Strings in Java
How to Use 3Des Encryption/Decryption in Java
Remove All Occurrences of Char from String
How to Call a Static Method in Jsp/El
Multiple Line Code Example in Javadoc Comment
How to Escape Special HTML Characters in Jsp
Parse JSON from Httpurlconnection Object
By Default, Jsf Generates Unusable Ids, Which Are Incompatible with the CSS Part of Web Standards