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-Range
header to obtain a range of bytes, then i need to write these bytes on a file
the 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 downloadPDFBlock
receive 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-53151
is downloaded first, these will be the bytes that will be written first in the file. I have read about BlockingObserver
but 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());
}
});
}