2015-06-29 12 views
5

Pobierany jest plik pdf z doposażeniem, sposób, w jaki go pobieraj, jest blokowany. Używam nagłówka Content-Range, aby uzyskać zakres bajtów, a następnie muszę zapisać te bajty na file problemem jest kolejność ich zapisywania. Używam funkcji flatMap(), aby zwrócić obserwowalne dla każdego żądania, które należy wykonać, aby pobrać plik.Pobierz i napisz plik z Retrofit i RxJava

.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 otrzymywać e struny, która jest potrzebne nagłówka: Range: bytes=0-3999. Następnie używam funkcji subskrypcji do zapisywania bajtów pobranych:

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); 
        } 
       } 
      }); 

Problem polega na tym, że proces pisania został nieuporządkowany. Na przykład: jeśli najpierw pobierze się Range: bytes=44959-53151, będą to bajty, które zostaną zapisane jako pierwsze w pliku. Przeczytałem o BlockingObserver, ale nie wiem, czy to może być rozwiązanie.

Mam nadzieję, że możesz mi pomóc.

+1

Sprawdź 'operator concatMap' http://fernandocejas.com/2015/01/11/rxjava-observable-tranformation-concatmap-vs-flatmap/ –

+1

Ewentualnie użyć RandomAccessFile i pisać w tym samym pliku offsetowego wniosek został złożony dla. – akarnokd

+0

Należy użyć narzędzia DownloadManager do ładowania dużej zawartości. http://developer.android.com/reference/android/app/DownloadManager.html –

Odpowiedz

8

Oto dobry example do pobrania pliku i zapisania go na dysku w systemie Android.

Oto modyfikacja powyższego połączonego przykładu bez użycia wyrażenia lambda.

Interfejs Retrofit 2, @Streaming do pobierania dużych plików.

public interface RetrofitApi { 
    @Streaming 
    @GET 
    Observable<Response<ResponseBody>> downloadFile(@Url String fileUrl); 
} 

Kod do pobrania pliku i zapisania go na dysk przy użyciu Retrofit 2 i rxjava. Zaktualizuj ścieżkę baseUrl i ścieżkę URL w poniższym kodzie do rzeczywistego adresu URL pliku, który musisz pobrać.

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()); 
          } 
         }); 
} 
+0

hej człowiek, tworzysz instancję serwera api za każdym razem, gdy pobierasz plik –

+2

Jest to demonstracja, instancja Retrofit może zostać przeniesiona do stanu globalnego. –

+0

Masz wklejanie jako parametr '@Url String fileUrl' ale wpisz ścieżkę' "resources/archive/important_files.zip" '... metoda wydaje się być' @Streaming @GET ({path}) Observable > downloadFile (@Path ("ścieżka") String fileUrl); ' – NickUnuchek