我使用 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。代码有什么问题吗?
第一种方式
我尝试了很多代码,最后我用 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);
}
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”标头并将流附加到已下载的文件中。
根据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);
}
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();}
}
}
}
});