我正在通过改造下载pdf文件,我下载的方式是按块进行。我使用Content-Range
标头获取一个字节范围,然后我需要在file
上写这些字节,问题是编写它们的顺序。我使用flatMap()
函数为每个下载文件必须完成的请求返回一个observable。
.flatMap(new Func1<Integer, Observable<Response>>() {
@Override
public Observable<Response> call(Integer offset) {
int end;
if (offset + BLOCK_SIZE > (contentLength - 1))
end = (int) contentLength - 1 - offset;
else
end = offset + BLOCK_SIZE;
String range = getResources().getString(R.string.range_format, offset, end);
return ApiAdapter.getApiService().downloadPDFBlock(range);
}
})
downloadPDFBlock
会收到标题所需的字符串:Range: bytes=0-3999
。然后我用subscribe函数写下载的字节
subscribe(new Subscriber<Response>() {
@Override
public void onCompleted() {
Log.i(LOG_TAG, file.getAbsolutePath());
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(Response response) {
writeInCache(response);
}
}
});
但问题是写作过程是无序的。例如:如果首先下载Range: bytes=44959-53151
,则这些将是首先在文件中写入的字节。我读过有关BlockingObserver
的内容,但我不知道这是否可以解决。
我希望你能帮助我。
答案 0 :(得分:14)
这是一个很好的example,用于下载文件并将其保存到Android中的磁盘。
以下是对上述链接示例的修改,而不使用lambda表达式。
Retrofit 2界面,@ Streaming用于下载大文件。
public interface RetrofitApi {
@Streaming
@GET
Observable<Response<ResponseBody>> downloadFile(@Url String fileUrl);
}
使用Retrofit 2和rxjava下载文件并将其保存到磁盘的代码。将下面代码中的baseUrl和url路径更新为您需要下载的文件的实际URL。
public void downloadZipFile() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://my.resources.com/")
.client(new OkHttpClient.Builder().build())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();
RetrofitApi downloadService = retrofit.create(RetrofitApi.class);
downloadService.downloadFile("resources/archive/important_files.zip")
.flatMap(new Func1<Response<ResponseBody>, Observable<File>>() {
@Override
public Observable<File> call(final Response<ResponseBody> responseBodyResponse) {
return Observable.create(new Observable.OnSubscribe<File>() {
@Override
public void call(Subscriber<? super File> subscriber) {
try {
// you can access headers of response
String header = responseBodyResponse.headers().get("Content-Disposition");
// this is specific case, it's up to you how you want to save your file
// if you are not downloading file from direct link, you might be lucky to obtain file name from header
String fileName = header.replace("attachment; filename=", "");
// will create file in global Music directory, can be any other directory, just don't forget to handle permissions
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsoluteFile(), fileName);
BufferedSink sink = Okio.buffer(Okio.sink(file));
// you can access body of response
sink.writeAll(responseBodyResponse.body().source());
sink.close();
subscriber.onNext(file);
subscriber.onCompleted();
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(e);
}
}
});
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<File>() {
@Override
public void onCompleted() {
Log.d("downloadZipFile", "onCompleted");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
Log.d("downloadZipFile", "Error " + e.getMessage());
}
@Override
public void onNext(File file) {
Log.d("downloadZipFile", "File downloaded to " + file.getAbsolutePath());
}
});
}