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

我正在下载一个改装的pdf文件,我下载它的方式是块。 我使用Content-Range标头来获取一个字节范围,然后我需要在file上写这些字节,问题是编写它们的顺序。 我正在使用flatMap()函数为每个下载文件必须完成的请求返回一个observable。

 .flatMap(new Func1<Integer, Observable>() { @Override public Observable 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() { @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但我不知道这是否可以解决问题。

我希望你能帮助我。

以下是下载文件并将其保存到Android磁盘的一个很好的示例 。

以下是不使用lambdaexpression式的上述链接示例的修改。

Retrofit 2界面,@ Streaming用于下载大文件。

 public interface RetrofitApi { @Streaming @GET Observable> 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, Observable>() { @Override public Observable call(final Response responseBodyResponse) { return Observable.create(new Observable.OnSubscribe() { @Override public void call(Subscriber 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() { @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()); } }); }