60
URL url = new URL("http://download.thinkbroadband.com/20MB.zip");

URLConnection connection = url.openConnection();
File fileThatExists = new File(path); 
OutputStream output = new FileOutputStream(path, true);
connection.setRequestProperty("Range", "bytes=" + fileThatExists.length() + "-");

connection.connect();

int lenghtOfFile = connection.getContentLength();

InputStream input = new BufferedInputStream(url.openStream());
byte data[] = new byte[1024];

long total = 0;

while ((count = input.read(data)) != -1) {
    total += count;

    output.write(data, 0 , count);
}

in this code I try to resume download. Target file is 20MB. But when I stop download on 10mb, then contunue, I get file with filesize 30MB. It seems that it continue writing to file, but cant partly download from server. Wget -c works great with this file. How can I resume file download?

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
POMATu
  • 3,422
  • 7
  • 30
  • 42
  • 2
    possible duplicate of [how to resume an interrupted download - part 2](http://stackoverflow.com/questions/3428102/how-to-resume-an-interrupted-download-part-2) – BalusC Jun 04 '11 at 13:51

7 Answers7

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

POMATu
  • 3,422
  • 7
  • 30
  • 42
  • 8
    Hello , 'ISSUE_DOWNLOAD_STATUS.intValue() ' i am not understand about this value please give me the reference for this. – DynamicMind Apr 04 '12 at 09:05
  • 1
    I anticipate that this code will not work well. If the condition of if(ISSUE_DOWNLOAD_STATUS.intValue()==ECMConstant.ECM_DOWNLOADING) fails, than the else{} statement will be executed where "downloaded" variable is undefined. – asiby Apr 07 '13 at 16:01
  • 4
    @asiby Let's assume we all can code a little bit, this code gives you a hint on how to solve the problem not a total fix. – Ceetn Aug 26 '13 at 07:56
  • 17
    Hey there @Ceetn, if you can't give any constructive help, then zip it. Most people here try hard to to provide functional solutions. This one looks like it originated from somewhere where it might be working, but there are missing variable declarations. I tried to used it, and I pretty much spent several minutes trying to understand it. What are ISSUE_DOWNLOAD_STATUS & ECMConstant? It may be clear for you. But you are totally wrong to assume that people can code when they are seeking help here. You would have reacted differently if you teachers had acted like that in your programming classes. – asiby Aug 27 '13 at 19:15
  • 1
    @asiby The condition in "if" statement just checks the state of the particular file (if it was paused before, and resume download required). This is a custom condition, so you should replace it with your own logic. – Alex Lipov Aug 09 '14 at 06:31
  • This snippet gives us the perfect idea how to go about resuming the downloads with few tweaks. Kudos!! – Anish Mar 19 '16 at 09:15
  • @POMATu you just pasted the code and ran away.. pls explain lil bit bro – Himanshu Mori Nov 24 '16 at 12:22
  • @POMATu i get `NotFoundException` for valid url and download links – tux-world Nov 27 '16 at 09:14
  • 4
    I don't get how this answer has more upvotes and also accepted. Lot of not explained things, outside references. Constants does not have value. downloaded property's value set in if branch, used in else branch. If and else branch is the same. Why setDoOutput is set to true? Any POST passed? Same buffer size used at 3 places, no constant used. No error handling at all. – HoGo Dec 30 '17 at 10:04
  • Please provide complete code if it is for resume then please mention pause function as well ...Thank you – Ashish Garg May 30 '20 at 11:55
18

I guess the problem you are facing is calling url.openStream() after url.openConnection().

url.openStream() is equivalent to url.openConnection().getInputStream(). Hence, you are requesting the url twice. Particularly the second time, it is not specifying the range property. Therefore download always starts at the beginning.

You should replace url.openStream() with connection.getInputStream().

Sufian
  • 6,405
  • 16
  • 66
  • 120
user1084090
  • 339
  • 3
  • 3
  • Thanks man very much. You really saved me! I encountered a similar problem and your solution was perfect. Thank you! – mohamede1945 Jul 29 '12 at 12:27
  • I was searching for my problem for like an hour, and you sir, you made my day and solved it! Thanks a lot! – mehrmoudi Sep 25 '12 at 11:14
7

This is what I am using to download the file in chunk Updating the UI with progress.

/*
 * @param callback = To update the UI with appropriate action
 * @param fileName = Name of the file by which downloaded file will be saved.
 * @param downloadURL = File downloading URL
 * @param filePath = Path where file will be saved
 * @param object = Any object you want in return after download is completed to do certain operations like insert in DB or show toast
 */

public void startDownload(final IDownloadCallback callback, String fileName, String downloadURL, String filePath, Object object) {
    callback.onPreExecute(); // Callback to tell that the downloading is going to start
    int count = 0;
    File outputFile = null; // Path where file will be downloaded
    try {
        File file = new File(filePath);
        file.mkdirs();
        long range = 0;
        outputFile = new File(file, fileName);
        /**
         * Check whether the file exists or not
         * If file doesn't exists then create the new file and range will be zero.
         * But if file exists then get the length of file which will be the starting range,
         * from where the file will be downloaded
         */
        if (!outputFile.exists()) {
            outputFile.createNewFile();
            range = 0;
        } else {
            range = outputFile.length();
        }
        //Open the Connection
        URL url = new URL(downloadURL);
        URLConnection con = url.openConnection();
        // Set the range parameter in header and give the range from where you want to start the downloading
        con.setRequestProperty("Range", "bytes=" + range + "-");
        /**
         * The total length of file will be the total content length given by the server + range.
         * Example: Suppose you have a file whose size is 1MB and you had already downloaded 500KB of it.
         * Then you will pass in Header as "Range":"bytes=500000".
         * Now the con.getContentLength() will be 500KB and range will be 500KB.
         * So by adding the two you will get the total length of file which will be 1 MB
         */
        final long lenghtOfFile = (int) con.getContentLength() + range;

        FileOutputStream fileOutputStream = new FileOutputStream(outputFile, true);
        InputStream inputStream = con.getInputStream();

        byte[] buffer = new byte[1024];

        long total = range;
        /**
         * Download the save the content into file
         */
        while ((count = inputStream.read(buffer)) != -1) {
            total += count;
            int progress = (int) (total * 100 / lenghtOfFile);
            EntityDownloadProgress entityDownloadProgress = new EntityDownloadProgress();
            entityDownloadProgress.setProgress(progress);
            entityDownloadProgress.setDownloadedSize(total);
            entityDownloadProgress.setFileSize(lenghtOfFile);
            callback.showProgress(entityDownloadProgress);
            fileOutputStream.write(buffer, 0, count);
        }
        //Close the outputstream
        fileOutputStream.close();
        // Disconnect the Connection
        if (con instanceof HttpsURLConnection) {
            ((HttpsURLConnection) con).disconnect();
        } else if (con instanceof HttpURLConnection) {
            ((HttpURLConnection) con).disconnect();
        }
        inputStream.close();
        /**
         * If file size is equal then return callback as success with downlaoded filepath and the object
         * else return failure
         */
        if (lenghtOfFile == outputFile.length()) {
            callback.onSuccess(outputFile.getAbsolutePath(), object);
        } else {
            callback.onFailure(object);
        }
    } catch (Exception e) {
        e.printStackTrace();
        callback.onFailure(object);
    }
}

interface IDownloadCallback {

    void onPreExecute(); // Callback to tell that the downloading is going to start
    void onFailure(Object o); // Failed to download file
    void onSuccess(String path, Object o); // Downloaded file successfully with downloaded path
    void showProgress(EntityDownloadProgress entityDownloadProgress); // Show progress
}

public class EntityDownloadProgress {

    int progress; // range from 1-100
    long fileSize;// Total size of file to be downlaoded
    long downloadedSize; // Size of the downlaoded file

    public void setProgress(int progress) {this.progress = progress;}

    public void setFileSize(long fileSize) {this.fileSize = fileSize;}

    public void setDownloadedSize(long downloadedSize) {this.downloadedSize = downloadedSize;}
}
Community
  • 1
  • 1
Ankit Kumar
  • 276
  • 4
  • 6
3

I have a way for your code to work.

  1. First, check if the file exits or not
  2. If the file exists, set the connection:

    connection.setRequestProperty("Range", "bytes=" + bytedownloaded + "-");
    
  3. If file does not exist, do the same download in a new file.

Gowthaman M
  • 8,057
  • 8
  • 35
  • 54
3

How about this?

public static void download(DownloadObject object) throws IOException{
    String downloadUrl = object.getDownloadUrl();
    String downloadPath = object.getDownloadPath();
    long downloadedLength = 0;

    File file = new File(downloadPath);
    URL url = new URL(downloadUrl);

    BufferedInputStream inputStream = null;
    BufferedOutputStream outputStream = null;

    URLConnection connection = url.openConnection();

    if(file.exists()){
        downloadedLength = file.length();
        connection.setRequestProperty("Range", "bytes=" + downloadedLength + "-");
        outputStream = new BufferedOutputStream(new FileOutputStream(file, true));

    }else{
        outputStream = new BufferedOutputStream(new FileOutputStream(file));

    }

    connection.connect();

    inputStream = new BufferedInputStream(connection.getInputStream());


    byte[] buffer = new byte[1024*8];
    int byteCount;

    while ((byteCount = inputStream.read(buffer)) != -1){
        outputStream.write(buffer, 0, byteCount);
        break;

    }

    inputStream.close();
    outputStream.flush();
    outputStream.close();

}

Used break; to test the code.. ;)

Udara
  • 31
  • 2
  • 2
    In addition to providing some code, please add some text explaining why your code worked. – buczek Aug 26 '16 at 19:04
  • @buczek It is standard code to download files, except connect.setRequestProperty method and append property for FileOutputStream set to true. – Valeriy K. Dec 04 '19 at 08:42
  • Check the request in the postman, you may need to add a check that the server responds in parts or in full. I had to add: connection.setRequestProperty("Accept-Encoding", "") – Sergey Shamanayev Aug 09 '21 at 10:47
3

Check out this thread which has a problem similar to yours. If wget is working, then the server clearly supports resuming downloads. It looks like you're not setting the If-Range header as mentioned in the accepted answer of the above link. ie. add:

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

// ...

// Resume download.
connection.setRequestProperty("If-Range", lastModified); 
Community
  • 1
  • 1
Mark Fisher
  • 9,838
  • 3
  • 32
  • 38
  • With your code my app doesn't download anything. lastmodified string contains valid date (14 may 2008). What can it be? – POMATu Jun 05 '11 at 18:00
1

Since the question is tagged with Android: Have you tried using DownloadManager. It handles all this stuff nicely for you.

KRP
  • 294
  • 7
  • 22
koljaTM
  • 10,064
  • 2
  • 40
  • 42
  • Thanks, I'll switch to it, downloads should be much more stable now! – POMATu Oct 24 '12 at 19:27
  • Remember that it needs API level 9 (Android 2.3). I don't know if that's a problem. 2.2 is pretty much on the way out anyway. – koljaTM Oct 25 '12 at 06:22