5

As title says, I want to download file using RxJava2 and okhttp. And also process is needed.

In my opinion, the data flow is String url -> Response -> multiply Float process.

So first I need a Single which emits Response.

Single.create((SingleOnSubscribe<Response>) emitter -> {
    emitter.onSuccess(xx);// or throw error
});

Next I need Flowable right? Because I want to emit multiply process info to Observer.

So here how can I do ? Thanks in advance.

EDIT

I tried to convert Single to Flowable.

Single.create((SingleOnSubscribe<Response>) emitter -> {
    emitter.onSuccess(xxx);// or throw error
}).toFlowable()
        .flatMap((Function<Response, Publisher<Float>>) response -> Flowable.create(xxx))
        .subscribe(process -> {});

}

But I do not know whether it's suitable to do like this.

EDIT

I do not care about the okhttp details and I only care about something relevant to RxJava such as Single and Flowable.

CoXier
  • 2,523
  • 8
  • 33
  • 60
  • Emitting the `Response` might be too late already. You might want to have a look into a progress recipe which might be adaptable https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java – tynn May 05 '18 at 15:22
  • 1
    What do you mean by "process is needed"? You mean like showing download percentage in progressbar? – Dmitry May 11 '18 at 08:49
  • I need to get progroess and show it in UI. – CoXier May 12 '18 at 03:30
  • Here old sample of downloading file with okhttp and rxjava with progress. https://gist.github.com/zella/201c98693a1343ef489b7efae358db34 – zella Jul 11 '19 at 18:49
  • Created fully working solution here in Kotlin here: https://stackoverflow.com/a/68783778/5503940 – Antonis Radz Aug 14 '21 at 13:38

2 Answers2

1

This an example of how to do so with Retrofit 2, OkHttp, Okio and RxJava.

import com.squareup.okhttp.ResponseBody;
import okio.BufferedSink;
import okio.Okio;
import retrofit.Response;
import retrofit.http.GET;
import retrofit.http.Query;
import rx.Observable;

interface ApiService {
    @GET("/media/download")
    Observable<Response<ResponseBody>> download(@Query("id") String id);
}

public Observable<File> download(String id) {
    return service.download(id)
        .flatMap(this::saveFile);
}

public Observable<File> saveFile(Response<ResponseBody> response) {
    return Observable.create((Observable.OnSubscribe<File>) subscriber -> {
        try {
            // you can access headers of response
            String header = response.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_MUSIC)
                    .getAbsoluteFile(), fileName);

            // No space before "(Environment.DIRECTORY_MUSIC)"
            BufferedSink sink = Okio.buffer(Okio.sink(file));
            // you can access body of response
            sink.writeAll(response.body().source());
            sink.close();
            subscriber.onNext(file);
            subscriber.onCompleted();
        } catch (IOException e) {
            e.printStackTrace();
            subscriber.onError(e);
        }
    });
}
Mr-IDE
  • 7,051
  • 1
  • 53
  • 59
0

If you use retrofit you can try this https://github.com/imfms/retrofit-rxjava-request-with-progress

If not adapt one of underlying classes:

First use okhttp async api. Second use rx schedulers for async.

Anton Pogonets
  • 1,162
  • 7
  • 16