my below code for downloading file work fine when don't implementing resume
for that, after read more solution to implementing that and resolve problem i know i must check Last-Modified
header and set it for connection,
but i can't do that because of i get error such as android Cannot set request property after connection is made
or i get null
for httpURLConnection
,
i'm using this reference
getHeaderField
heaser return:
{
null=[HTTP/1.1 200 OK],
Cache-Control=[public],
Connection=[keep-alive],
Content-Length=[8037404],
Content-Md5=[VEqXHCc/Off7a6D0gRFpiQ==],
Content-Type=[image/jpeg],
Date=[Tue, 19 Jan 2016 07:24:36 GMT],
Etag=["544a971c273f39f7fb6ba0f481116989"],
Expires=[Sat, 29 Jul 2017 10:07:00 GMT],
Last-Modified=[Thu, 18 Dec 2014 08:44:34 GMT],
Server=[bws],
X-Android-Received-Millis=[1501063623576],
X-Android-Response-Source=[NETWORK 200],
X-Android-Selected-Protocol=[http/1.1],
X-Android-Sent-Millis=[1501063623532]
}
now how can i set that to have resume for download files?
GitHub Link
public void run() {
final URL url;
HttpURLConnection httpURLConnection = null;
try {
try {
url = new URL(mUrl);
String lastModified = httpURLConnection.getHeaderField("Last-Modified");
if (!lastModified.isEmpty()) {
httpURLConnection.setRequestProperty("If-Range", lastModified);
}
httpURLConnection = (HttpURLConnection) url.openConnection();
if (mFile.exists()) {
downloadedLength = mFile.length();
Log.e("downloadedLength ", downloadedLength + "");
httpURLConnection.setRequestProperty("Range", "bytes=" + downloadedLength + "-");
fileOutputStream = new FileOutputStream(mFile, true);
} else {
fileOutputStream = new FileOutputStream(mFile);
}
httpURLConnection.setConnectTimeout(30000);
httpURLConnection.setReadTimeout(30000);
httpURLConnection.setRequestMethod("GET");
} catch (IOException e) {
}
final int responseCode;
final int total;
try {
responseCode = httpURLConnection.getResponseCode();
total = httpURLConnection.getContentLength();
} catch (IOException e) {
e.printStackTrace();
Log.e("ER UPDATE ", e.getMessage());
}
if (responseCode == 200) {
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());
}
}
and also i get 206
response code instead of 200
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());
}
}
Take a look at this answer of POMATu.
But anyway, if You downloading files over the HTTP
protocol, You can use DownloadManager
- a system service (since API level 9) for long-running downloads in the background. It handles HTTP
connections, connectivity changes, reboots, and ensures each download completes successfully. And it already supports resuming and also progress notifications.
You can find many tutorials like this or examples like that, and many solutions on stackoverflow by android-download-manager tag.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With