【问题标题】:How to pause/resume downloading with okhttp in Android如何在 Android 中使用 okhttp 暂停/恢复下载
【发布时间】:2017-09-07 05:35:10
【问题描述】:

我使用 okhttp 库在 android 中下载文件。我下载成功。但是当我暂停并继续下载时出现问题。

Response request = new Request.Builder().url(url).build();
ResponseBody responseBody = response.body();

File file = new File(filePath);
BufferedInputStream input = new BufferedInputStream(responseBody.byteStream());
OutputStream output;

if (isResume) {
    output = new FileOutputStream(file, true);
    input.skip(downloadedSize);
} else {
    output = new FileOutputStream(file, false);    
}

long totalByteSize = responseBody.contentLength();
byte[] data = new byte[1024];
int count = 0;

while ((count = input.read(data)) != -1) {
    downloadedSize += count;
    output.write(data, 0, count);   
}

问题是,例如文件大小为 10MB。我在下载 3MB 时暂停,然后继续下载,当下载完成时文件大小变为 13MB。它不是从恢复时下载的大小开始,而是从字节流的开头开始下载。所以文件变成13MB。代码有什么问题?

【问题讨论】:

  • 您正在创建一个新请求并发送它。这会导致响应保存整个文件。我不确定 okHttp 是否有能力下载部分响应,但如果有,那不是你的做法。您至少必须传入一些关于从何处开始下载的数据。
  • 我认为您的方法没有任何问题,可能是您的下载大小为 0,而这又不跳过任何内容。我真的没有发现问题和第一种方式描述的答案之间有任何区别。如果有任何具体区别,请告诉我
  • @Killer 的区别是 source.skip(downloadedSize) 。您看不到的代码我从 db 获取文件下载状态和下载大小。例如文件大小为 5.5mb,我在过去的下载过程中下载了 3mb。下一个下载过程必须调用source.skip(3mb to bytes)

标签: android download okhttp bytestream


【解决方案1】:

第一路

我尝试了很多代码,最后我解决了 BufferedSource source = responseBody.source(); source.skip(downloadedSize);

Response request = new Request.Builder().url(url).build();
ResponseBody responseBody = response.body();
BufferedSource source = responseBody.source();

if(isResume)
    source.skip(downloadedSize);

File file = new File(filePath);
BufferedInputStream input = new BufferedInputStream(responseBody.byteStream());
OutputStream output;

if (isResume) {
    output = new FileOutputStream(file, true);
} else {
    output = new FileOutputStream(file, false);    
}

long currentDownloadedSize = 0;
long currentTotalByteSize = responseBody.contentLength();
byte[] data = new byte[1024];
int count = 0;
while ((count = input.read(data)) != -1) {
    currentDownloadedSize += count;
    output.write(data, 0, count);   
}

成功了。我想我很幸运:)

第二种方式

我为跳过下载的字节添加了标头,它起作用了。

Request.Builder requestBuilder = new Request.Builder();
if (isResume) {
    requestBuilder.addHeader("Range", "bytes=" + String.valueOf(downloadedSize) + "-");
}
Response request = requestBuilder.url(url).build();
ResponseBody responseBody = response.body();
BufferedSource source = responseBody.source();

File file = new File(filePath);
BufferedInputStream input = new BufferedInputStream(responseBody.byteStream());
OutputStream output;

if (isResume) {
    output = new FileOutputStream(file, true);
} else {
    output = new FileOutputStream(file, false);    
}

long currentDownloadedSize = 0;
long currentTotalByteSize = responseBody.contentLength();
byte[] data = new byte[1024];
int count = 0;
while ((count = input.read(data)) != -1) {
    currentDownloadedSize += count;
    output.write(data, 0, count);   
}

【讨论】:

  • 首先如何暂停下载?你能分享一些关于如何处理按钮点击的简历部分的想法吗?
  • @dazed'n'confused 抱歉,但我无法理解您的问题。恢复/暂停有什么问题?
  • 我正在尝试使用服务下载文件,我需要在服务中暂停下载,我该怎么做?
  • @dazed'n'confused 您需要在文件暂停时保存上次下载的文件大小。在恢复时,您将使用下载的大小从哪个字节开始下载。其实你的问题的答案在我的回答stackoverflow.com/a/43356615/1135367
  • 第二种方式更好,第一种方式使用skip时,客户端必须先下载文件,然后从第一个跳过N字节。例如,您开始下载 100 MB 的文件并以 50 MB 的速度继续下载,当您从第一次开始客户端下载但跳过 50 MB 并且在 50 MB 之后您可以读取输出。
【解决方案2】:
val call = client.newCall(request)

call.enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        listener.onFail(e)
    }

    override fun onResponse(call: Call, response: Response) {
        // write the stream to a file (downloading happening)
        val stream = response.body?.byteStream()
    }
})

// this will stop the downloading
call.cancel()

要继续在您的请求中使用“Range”标头,并将流附加到已下载的文件中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-26
    • 2011-01-03
    • 2012-08-27
    • 2011-02-27
    相关资源
    最近更新 更多