java 下载并使用 Retrofit 和 RxJava 编写文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31126793/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Download and write a file with Retrofit and RxJava
提问by Silmood
I'm downloading a pdf file with retrofit, the way that i'm downloading it is by blocks. I use the Content-Rangeheader to obtain a range of bytes, then i need to write these bytes on a filethe problem is the order to write them. I'm using the flatMap()function to return an observable for each request that must be done to download the file.
我正在下载一个带有改造的 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);
}
})
The downloadPDFBlockreceive an strings that is needed by a header : Range: bytes=0-3999. Then i use subscribe function to write the bytes downloaded
在downloadPDFBlock接收由一个标题需要一个字符串:Range: bytes=0-3999。然后我使用订阅功能来写入下载的字节
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);
}
}
});
But the problem is that the writing process is made unordered. For example: if the Range: bytes=44959-53151is downloaded first, these will be the bytes that will be written first in the file. I have read about BlockingObserverbut i don't know if that could be a solution.
但问题是写入过程是无序的。例如:如果Range: bytes=44959-53151首先下载,这些将是首先写入文件的字节。我已经读过,BlockingObserver但我不知道这是否可以成为解决方案。
I hope you could help me.
我希望你能帮助我。
回答by s-hunter
Here is a good examplefor downloading a file and save it to disk in Android.
这是在 Android 中下载文件并将其保存到磁盘的一个很好的示例。
Here is a modification of the above linked example without using the lambda expression.
这是对上述链接示例的修改,不使用 lambda 表达式。
The Retrofit 2 interface, the @Streaming for downloading large files.
Retrofit 2 界面,用于下载大文件的@Streaming。
public interface RetrofitApi {
@Streaming
@GET
Observable<Response<ResponseBody>> downloadFile(@Url String fileUrl);
}
The code for downloading a file and saving it to disk using Retrofit 2 and rxjava. Update the baseUrl and the url path in the code below to your actual url of the file you need to download.
使用 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());
}
});
}

