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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 18:09:02  来源:igfitidea点击:

Download and write a file with Retrofit and RxJava

javaandroidfile-ioretrofitrx-java

提问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());
                            }
                       });
}