13

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

Andrii Omelchenko
  • 13,183
  • 12
  • 43
  • 79
DolDurma
  • 15,753
  • 51
  • 198
  • 377
  • 1
    Why You don't use [`DownloadManager`](https://developer.android.com/reference/android/app/DownloadManager.html), which "download in the background, taking care of HTTP interactions and retrying downloads after failures or across connectivity changes and system reboots" and already implements 'resume'? – Andrii Omelchenko Aug 01 '17 at 08:58
  • @AndriiOmelchenko for this implementation i can't find any good document – DolDurma Aug 01 '17 at 09:38
  • DownloadManager is exactly for downloading files and it's work fine. Try [this](https://www.androidtutorialpoint.com/networking/android-download-manager-tutorial-download-file-using-download-manager-internet/) example. – Andrii Omelchenko Aug 01 '17 at 09:51

2 Answers2

5

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());
    }
}
Yazan
  • 6,074
  • 1
  • 19
  • 33
  • @Mahdi.Pishguy , yes you are right, i was not looking into your code (logic wise) now i did, i found that "if-range" does not make a lot of sense, so check my new code , look at **EDIT** – Yazan Aug 02 '17 at 06:32
  • i get this error `java.lang.IllegalStateException: Already connected` for this line `httpURLConnection.setDoInput(true);`. i commented `httpURLConnection.connect();`, repository on git updated – DolDurma Aug 02 '17 at 07:35
  • @Mahdi.Pishguy remove it, i placed a comment for you, to delete it if it cause a problem. – Yazan Aug 02 '17 at 07:44
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/150788/discussion-between-mahdi-pishguy-and-yazan). – DolDurma Aug 02 '17 at 07:53
  • hi man, could you help me on [this topic](https://stackoverflow.com/q/46260590/1830228)? Thanks – DolDurma Sep 17 '17 at 05:20
1

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 tag.

Andrii Omelchenko
  • 13,183
  • 12
  • 43
  • 79