20

I'm trying to implement pause/resume in my download manager, I search the web and read several articles and change my code according them but resume seems not working correctly, Any ideas?

                if (!downloadPath.exists()) 
                    downloadPath.mkdirs(); 

                if (outputFileCache.exists())
                {
                    downloadedSize = outputFileCache.length();
                    connection.setAllowUserInteraction(true);
                    connection.setRequestProperty("Range", "bytes=" + downloadedSize + "-");
                    connection.setConnectTimeout(14000);
                    connection.connect();
                    input = new BufferedInputStream(connection.getInputStream());
                    output = new FileOutputStream(outputFileCache, true);
                    input.skip(downloadedSize); //Skip downloaded size
                }
                else
                {
                    connection.setConnectTimeout(14000);
                    connection.connect();
                    input = new BufferedInputStream(url.openStream());
                    output = new FileOutputStream(outputFileCache);
                }

                fileLength = connection.getContentLength();                 


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

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

                }
                output.flush();
                output.close();
                input.close();
Ali
  • 2,012
  • 3
  • 25
  • 41

5 Answers5

21

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();
       }
Ali
  • 2,012
  • 3
  • 25
  • 41
5

It is impossible to tell what is wrong without some more information, however things to note:

  1. You must make a HTTP/1.1 request (it's hard to tell from your sample code)
  2. The server must support HTTP/1.1
  3. The server will tell you what it supports with an Accept-Ranges header in the response
  4. If-Range should be the etag the server gave you for the resource, not the last modified time

You should check your range request with something simple to test the origin actually supports the Range request first (like curl or wget )

stringy05
  • 6,511
  • 32
  • 38
2

It nay be that your server is taking to long to respond (more then the timeout limit) or this is also a fact that not all servers support pause - resume. It is also a point to ponder that weather the file is downloaded through Http, https, ftp or udp.

Pausing" could just mean reading some of the stream and writing it to disk. When resuming you would have to use the headers to specify what is left to download.

you may try something like :

 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);
}

and please try to sync things.

Manan Sharma
  • 631
  • 2
  • 14
  • 28
  • Don't use `connection.setDoInput(true);` and `connection.setDoOutput(true);`, it will cause another problem. – Ali Mar 21 '13 at 11:35
  • You need to set it to true if you want to send (output) a request body, for example with POST or PUT requests. With GET, you do not usually send a body, so you do not need it. And similarly to receive a body with the incoming response you need to set input to true. Sending the request body itself is done via the connection's output stream: conn.getOutputStream().write(someBytes); – Manan Sharma Mar 21 '13 at 13:20
  • I don't think that '__progress = (int) (total * 100 / fileLength);' is doing something wrong – Manan Sharma Mar 21 '13 at 13:25
1

I would start debugging from this line:

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

As from the source code it is not possible to determine what downloadedSize is, it's hard to elaborate further, but the format should be bytes=from-to.

Anyway, I would suggest you to use Apache HttpClient to avoid common pitfalls. Here is a question from someone who uses Apache HttpClient on a similar topic and some sample code is provided.

Community
  • 1
  • 1
mindas
  • 26,463
  • 15
  • 97
  • 154
1

I think you just need to delete the input.skip(downloadedSize) line. Setting the HTTP header for byte range means the server will skip sending those bytes.

Say you have a file that's 20 bytes long consisting of "aaaaabbbbbcccccddddd", and suppose the transfer is paused after downloading 5 bytes. Then the Range header will cause the server to send "bbbbbcccccddddd", you should read all of this content and append it to the file -- no skip(). But the skip() call in your code will skip "bbbbb" leaving "cccccddddd" to be downloaded. If you've already downloaded at least 50% of the file, then skip() will exhaust all of the input and nothing will happen.

Also, all of the things in stringy05's post apply. You should make sure the server supports HTTP/1.1, make sure the Range header is supported for the resource (dynamically generated content may not support it), and make sure the resource isn't modified using etag and modification date.

picomancer
  • 1,786
  • 13
  • 15