如何在Android中使用okhttp暂停/恢复下载

问题描述 投票:0回答:4

我使用 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。代码有什么问题吗?

android download okhttp bytestream
4个回答
24
投票

第一种方式

我尝试了很多代码,最后我用 BufferedSource source = responseBody.source(); 解决了source.skip(下载的大小);

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

1
投票
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”标头并将流附加到已下载的文件中。


0
投票

根据Alexander的回答,我尝试了他推荐的两种方法。第一种方法在下载停止时产生错误,然后在应用程序重新启动后继续下载。我发现第二种方式更稳定。代码中还存在语法错误。示例代码在这里。

        File file = new File(path);
        long availableFileLength = file.exists() && file.isFile() ? file.length() :0;
        Request.Builder requestBuilder = new Request.Builder();
        requestBuilder.addHeader("Range", "bytes=" + String.valueOf(availableFileLength) + "-");
        Request request = requestBuilder.url(url).build();
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .readTimeout(30, TimeUnit.SECONDS)
            .connectTimeout(30, TimeUnit.SECONDS)
            .build();
        ResponseBody responseBody = okHttpClient.newCall(request).execute().body();

        InputStream input = new BufferedInputStream(responseBody.byteStream());
        OutputStream output = new FileOutputStream(downloadPath, true);

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

0
投票

我如何使用 OkHttp + Okio 做到这一点

        Request.Builder requestBuilder = new Request.Builder();
        if(isResume) requestBuilder.addHeader("Range","bytes="+download.current_size+"-");
        okHttpClient.newCall(requestBuilder.url(download.url).tag(download.file_name).build()).enqueue(new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {e.printStackTrace();}
            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) {
                if(response.isSuccessful()){
                    BufferedSink sink=null; BufferedSource source=null;
                    try {
                        ResponseBody responseBody = response.body();assert responseBody != null;
                        source = responseBody.source(); File downloadFile = new File(download.file_path);
                        sink = Okio.buffer(downloadFile.exists() ? Okio.appendingSink(downloadFile) : Okio.sink(downloadFile));
                        int bufferSize = 8 * 1024; long updateTime = 0;
                        for (long bytesRead; (bytesRead = source.read(sink.getBuffer(), bufferSize)) != -1; ) {
                            sink.emit();
                        }
                        Log.d(TAG, "File successfully downloaded and saved to storage.");
                    }
                    catch (IOException e){e.printStackTrace();}
                    finally {
                        try {if(sink!=null) {sink.flush();sink.close();} if(source != null)source.close();}catch (IOException e){e.printStackTrace();}
                    }
                }
            }
        });
© www.soinside.com 2019 - 2024. All rights reserved.