如何使用RandomAccessFile恢复中断的下载?

huangapple go评论107阅读模式
英文:

How to resume a interrupt download with RandomAccessFile?

问题

这段代码中,我想要使用RandomAccessFile来实现中断下载。以下是代码部分:

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

HttpRequest返回206,下载成功完成,但是我得到了一个错误的文件。该文件比源文件要大得多,而且我无法打开它。这段代码有什么问题?如何使用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?

答案1

得分: 2

> ```lang-none
> 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
> ```

请求指定了起始位置为 `32,243,658`,并下载了剩余的 `210,930,449` 字节,最终得到一个大小为 `243,174,107` 字节的文件。

实际上,文件的大小至少为 `243,204,042` 字节,即多了 `29,935` 字节,因为代码总是写入整个 `buffer`,即使缓冲区没有被完全读取。

很可能文件要比这大得多,因为数据是以较小的块通过网络传输的,所以在许多 `read()` 调用中缓冲区可能没有被填满到 `65,536` 字节。

`read()` 会返回一个 `len` 值,这是有原因的。你的代码应该是:

    writeFile.write(buf, 0, len);

此外,你应该使用 try-with-resources,并且通常会内联 `read()` 调用,这样它就不会重复,例如代码应该是:

```java
int totalLen = 0;
try (RandomAccessFile writeFile = new RandomAccessFile(desc, "rw")) {
    writeFile.seek(position); // 这里应该使用响应头中的值,而不是请求的值
    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();

<details>
<summary>英文:</summary>

From [comment][1]:

 &gt; ```lang-none
 &gt; Accept-Ranges: bytes
 &gt; ETag: W/&quot;243174107-1583744547157&quot;
 &gt; Last-Modified: Mon, 09 Mar 2020 09:02:27 GMT
 &gt; Content-Range: bytes 32243658-243174106/243174107
 &gt; Content-Type: application/zip
 &gt; Content-Length: 210930449
 &gt; ```

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&#39;t read in full.

It&#39;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&#39;s likely that the buffer isn&#39;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&#39;s common to inline the `read()` call, so it&#39;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();



  [1]: https://stackoverflow.com/questions/61075227/how-to-resume-a-interrupt-download-with-randomaccessfile?noredirect=1#comment108052236_61075227

</details>



huangapple
  • 本文由 发表于 2020年4月7日 15:51:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/61075227.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定