Search code examples
javadownloadrandomaccessfile

How to resume a interrupt download with RandomAccessFile?


I want to a interrupt download with RandomAccessFile. Here is the code

HttpGet httpget = HttpUtil.buildGet(URL)
httpget.setHeader("RANGE", "bytes=" + (position) + "-");
CloseableHttpResponse response = HttpClients.createDefault().execute(httpget) 
HttpEntity entity = response.getEntity();

int totalLen = 0;
InputStream is = entity.getContent();
RandomAccessFile writeFile = new RandomAccessFile(desc, "rw");
writeFile.seek(position);
byte[] buf = new byte[65536];
totalLen = 0;
int len = is.read(buf);
while (len > 0) {
   totalLen += len;
   writeFile.write(buf);
   len = is.read(buf);
}
response.close();
is.close();
writeFile.close();

The HttpRequest return 206 and the download success completed,But I get a wrong file,The file is much bigger than the source and I cant open it. What's wrong with this code?And how to resume a interrupt download with RandomAccessFile?


Solution

  • From comment:

    Accept-Ranges: bytes
    ETag: W/"243174107-1583744547157"
    Last-Modified: Mon, 09 Mar 2020 09:02:27 GMT
    Content-Range: bytes 32243658-243174106/243174107
    Content-Type: application/zip
    Content-Length: 210930449
    

    The request specified a starting position of 32,243,658, and downloaded the remaining 210,930,449 bytes, ending up with a file with 243,174,107 bytes.

    Well no, it ended up with a file with at least 243,204,042 bytes, i.e. 29,935 bytes too many, because the code always writes the full buffer, even if the buffer wasn't read in full.

    It's very likely that the file is a lot bigger than that, because the data is coming over the network in smaller chunks, so it's likely that the buffer isn't filled with 65,536 bytes on many of the read() call.

    read() returns a len value for a reason. Your code should be:

    writeFile.write(buf, 0, len);
    

    Also, you should use try-with-resources, and it's common to inline the read() call, so it's not repeated, e.g. code should be:

    int totalLen = 0;
    try (RandomAccessFile writeFile = new RandomAccessFile(desc, "rw")) {
        writeFile.seek(position); // should use value from response header here, not requested value
        try (InputStream is = entity.getContent()) {
            byte[] buf = new byte[65536];
            for (int len; (len = is.read(buf)) > 0; ) {
                totalLen += len;
                writeFile.write(buf, 0, len);
            }
        }
    }
    response.close();