使用Retrofit和RxJava下载并编写文件

时间:2015-06-29 22:43:27

标签: java android file-io retrofit rx-java

我正在通过改造下载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的内容,但我不知道这是否可以解决。

我希望你能帮助我。

1 个答案:

答案 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());
                            }
                       });
}