Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


¿Es posible mostrar la barra de progreso cuando se carga la imagen a través de Retrofit 2

Actualmente Retrofit 2 usando Retrofit 2 y quiero subir algunas fotos a mi servidor. Ya sé, que la versión anterior usa la clase TypedFile para cargar. Y si queremos usar la barra de progreso con ella debemos anular el método TypedFile en la clase TypedFile .

¿Es posible mostrar progreso al usar la biblioteca retrofit 2 ?

  • Dimensionar un TextView de múltiples líneas de Android
  • En Java, cómo obtener cadenas de días de la semana (Sun, Mon, ..., Sat) con el sistema de configuración por defecto (idioma)
  • Android DatePicker muestra meses no disponibles cuando se usan límites min / max
  • Android: muestra el micrófono sin grabación para obtener la amplitud / nivel en vivo?
  • Pregunta de Android: WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
  • Android spinner cerrar
  • ListViewDraggingAnimation roto en Android 5 Lollipop
  • ¿Es posible crear pegatinas personalizadas de Viber?
  • Android Studio - processDebugResources ha fallado
  • Separador de relleno, ignorándolo
  • ¿Los usuarios tienen que conceder el permiso com.android.vending.BILLING en la actualización / instalación?
  • ¿Cómo obtener la compensación horaria en GMT (como GMT + 7: 00) desde el dispositivo Android?
  • 8 Solutions collect form web for “¿Es posible mostrar la barra de progreso cuando se carga la imagen a través de Retrofit 2”

    En primer lugar, debe utilizar Retrofit 2 versión igual o superior a 2,0 beta2. En segundo lugar, crear nueva clase extends RequestBody :

      public class ProgressRequestBody extends RequestBody { private File mFile; private String mPath; private UploadCallbacks mListener; private static final int DEFAULT_BUFFER_SIZE = 2048; public interface UploadCallbacks { void onProgressUpdate(int percentage); void onError(); void onFinish(); } public ProgressRequestBody(final File file, final UploadCallbacks listener) { mFile = file; mListener = listener; } @Override public MediaType contentType() { // i want to upload only images return MediaType.parse("image/*"); } @Override public long contentLength() throws IOException { return mFile.length(); } @Override public void writeTo(BufferedSink sink) throws IOException { long fileLength = mFile.length(); byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; FileInputStream in = new FileInputStream(mFile); long uploaded = 0; try { int read; Handler handler = new Handler(Looper.getMainLooper()); while ((read = in.read(buffer)) != -1) { // update progress on UI thread handler.post(new ProgressUpdater(uploaded, fileLength)); uploaded += read; sink.write(buffer, 0, read); } } finally { in.close(); } } private class ProgressUpdater implements Runnable { private long mUploaded; private long mTotal; public ProgressUpdater(long uploaded, long total) { mUploaded = uploaded; mTotal = total; } @Override public void run() { mListener.onProgressUpdate((int)(100 * mUploaded / mTotal)); } } } 

    En tercer lugar, crear interfaz

     @Multipart @POST("/upload") Call<JsonObject> uploadImage(@Part MultipartBody.Part file); 

    Ahora puede obtener el progreso de su subida.

    En su activity (o fragment ):

     class MyActivity extends AppCompatActivity implements ProgressRequestBody.UploadCallbacks { ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); progressBar = findViewById(R.id.progressBar); ProgressRequestBody fileBody = new ProgressRequestBody(file, this); MultipartBody.Part filePart = MultipartBody.Part.createFormData("image", file.getName(), fileBody); Call<JsonObject> request = RetrofitClient.uploadImage(filepart); request.enqueue(new Callback<JsonObject>{...}); } @Override public void onProgressUpdate(int percentage) { // set current progress progressBar.setProgress(percentage); } @Override public void onError() { // do something on error } @Override public void onFinish() { // do something on upload finished // for example start next uploading at queue progressBar.setProgress(100); } } 

    A continuación, le indicamos cómo manejar el progreso del archivo de subida con un POST simple en lugar de Multipart. Para el cheque multipartido hacia fuera @ la solución de Yariy. Además, esta solución utiliza URI de contenido en lugar de referencias directas de archivos.

    RestClient

     @Headers({ "Accept: application/json", "Content-Type: application/octet-stream" }) @POST("api/v1/upload") Call<FileDTO> uploadFile(@Body RequestBody file); 

    ProgressRequestBody

     public class ProgressRequestBody extends RequestBody { private static final String LOG_TAG = ProgressRequestBody.class.getSimpleName(); public interface ProgressCallback { public void onProgress(long progress, long total); } public static class UploadInfo { //Content uri for the file public Uri contentUri; // File size in bytes public long contentLength; } private WeakReference<Context> mContextRef; private UploadInfo mUploadInfo; private ProgressCallback mListener; private static final int UPLOAD_PROGRESS_BUFFER_SIZE = 8192; public ProgressRequestBody(Context context, UploadInfo uploadInfo, ProgressCallback listener) { mContextRef = new WeakReference<>(context); mUploadInfo = uploadInfo; mListener = listener; } @Override public MediaType contentType() { // NOTE: We are posting the upload as binary data so we don't need the true mimeType return MediaType.parse("application/octet-stream"); } @Override public void writeTo(BufferedSink sink) throws IOException { long fileLength = mUploadInfo.contentLength; byte[] buffer = new byte[UPLOAD_PROGRESS_BUFFER_SIZE]; InputStream in = in(); long uploaded = 0; try { int read; while ((read = in.read(buffer)) != -1) { mListener.onProgress(uploaded, fileLength); uploaded += read; sink.write(buffer, 0, read); } } finally { in.close(); } } /** * WARNING: You must override this function and return the file size or you will get errors */ @Override public long contentLength() throws IOException { return mUploadInfo.contentLength; } private InputStream in() throws IOException { InputStream stream = null; try { stream = getContentResolver().openInputStream(mUploadInfo.contentUri); } catch (Exception ex) { Log.e(LOG_TAG, "Error getting input stream for upload", ex); } return stream; } private ContentResolver getContentResolver() { if (mContextRef.get() != null) { return mContextRef.get().getContentResolver(); } return null; } } 

    Para iniciar la subida:

     // Create a ProgressRequestBody for the file ProgressRequestBody requestBody = new ProgressRequestBody( getContext(), new UploadInfo(myUri, fileSize), new ProgressRequestBody.ProgressCallback() { public void onProgress(long progress, long total) { //Update your progress UI here //You'll probably want to use a handler to run on UI thread } } ); // Upload mRestClient.uploadFile(requestBody); 

    Advertencia, si se olvida de anular la función contentLength () puede recibir algunos errores oscuros:

     retrofit2.adapter.rxjava.HttpException: HTTP 503 client read error 

    O

     Write error: ssl=0xb7e83110: I/O error during system call, Broken pipe 

    O

     javax.net.ssl.SSLException: Read error: ssl=0x9524b800: I/O error during system call, Connection reset by peer 

    Estos son un resultado de RequestBody.writeTo () que se llama varias veces como el contenido predeterminado () es -1.

    De todas formas esto tomó mucho tiempo para averiguar, espero que ayude.

    Enlaces útiles: https://github.com/square/retrofit/issues/1217

    Actualizo la barra de progreso onProgressUpdate. Este código puede obtener un mejor rendimiento.

     @Override public void writeTo(BufferedSink sink) throws IOException { long fileLength = mFile.length(); byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; FileInputStream in = new FileInputStream(mFile); long uploaded = 0; try { int read; Handler handler = new Handler(Looper.getMainLooper()); int num = 0; while ((read = in.read(buffer)) != -1) { int progress = (int) (100 * uploaded / fileLength); if( progress > num + 1 ){ // update progress on UI thread handler.post(new ProgressUpdater(uploaded, fileLength)); num = progress; } uploaded += read; sink.write(buffer, 0, read); } } finally { in.close(); } } 

    Yuriy Kolbasinskiy modificado para usar rxjava y usar kotlin. Se agregó una solución para utilizar HttpLoggingInterceptor al mismo tiempo

     class ProgressRequestBody : RequestBody { val mFile: File val ignoreFirstNumberOfWriteToCalls : Int constructor(mFile: File) : super(){ this.mFile = mFile ignoreFirstNumberOfWriteToCalls = 0 } constructor(mFile: File, ignoreFirstNumberOfWriteToCalls : Int) : super(){ this.mFile = mFile this.ignoreFirstNumberOfWriteToCalls = ignoreFirstNumberOfWriteToCalls } var numWriteToCalls = 0 protected val getProgressSubject: PublishSubject<Float> = PublishSubject.create<Float>() fun getProgressSubject(): Observable<Float> { return getProgressSubject } override fun contentType(): MediaType { return MediaType.parse("video/mp4") } @Throws(IOException::class) override fun contentLength(): Long { return mFile.length() } @Throws(IOException::class) override fun writeTo(sink: BufferedSink) { numWriteToCalls++ val fileLength = mFile.length() val buffer = ByteArray(DEFAULT_BUFFER_SIZE) val `in` = FileInputStream(mFile) var uploaded: Long = 0 try { var read: Int read = `in`.read(buffer) while (read != -1) { uploaded += read.toLong() sink.write(buffer, 0, read) read = `in`.read(buffer) // when using HttpLoggingInterceptor it calls writeTo and passes data into a local buffer just for logging purposes. // the second call to write to is the progress we actually want to track if (numWriteToCalls > ignoreFirstNumberOfWriteToCalls ) { val progress = (uploaded.toFloat() / fileLength.toFloat()) * 100f //prevent publishing too many updates, which slows upload, by checking if the upload has progressed by at least 1 percent if (progress - lastProgressPercentUpdate > 1 || progress == 100f) { // publish progress getProgressSubject.onNext(progress) lastProgressPercentUpdate = progress } } } } finally { `in`.close() } } companion object { private val DEFAULT_BUFFER_SIZE = 2048 } 

    }

    Un ejemplo de interfaz de carga de vídeo

     public interface Api { @Multipart @POST("/upload") Observable<ResponseBody> uploadVideo(@Body MultipartBody requestBody); } 

    Una función de ejemplo para publicar un video:

     fun postVideo(){ val api : Api = Retrofit.Builder() .client(OkHttpClient.Builder() //.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .build()) .baseUrl("BASE_URL") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() .create(Api::class.java) val videoPart = ProgressRequestBody(File(VIDEO_URI)) //val videoPart = ProgressRequestBody(File(VIDEO_URI), 1) //HttpLoggingInterceptor workaround val requestBody = MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("example[name]", place.providerId) .addFormDataPart("example[video]","video.mp4", videoPart) .build() videoPart.getProgressSubject() .subscribeOn(Schedulers.io()) .subscribe { percentage -> Log.i("PROGRESS", "${percentage}%") } var postSub : Disposable?= null postSub = api.postVideo(requestBody) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ r -> },{e-> e.printStackTrace() postSub?.dispose(); }, { Toast.makeText(this,"Upload SUCCESS!!",Toast.LENGTH_LONG).show() postSub?.dispose(); }) } 

    Por lo que puedo ver en este post, no se han hecho actualizaciones sobre la respuesta de progreso de carga de imagen y todavía tiene que override el método writeTo como se muestra en esta respuesta de SO haciendo una interfaz ProgressListener y usando una subclase de TypedFile a writeTo método writeTo .

    Por lo tanto, no hay ninguna forma integrada para mostrar el progreso al utilizar la biblioteca de actualización 2.

    Intenté usar el código anterior, pero encontré que la interfaz de usuario se estaba atascando así que intenté este código, esto está funcionando para mí o puede intentar usar este código

    @ Luca992 Gracias por su respuesta. He implementado esto en JAVA y ahora está funcionando bien.

     public class ProgressRequestBodyObservable extends RequestBody { File file; int ignoreFirstNumberOfWriteToCalls; int numWriteToCalls;`enter code here` public ProgressRequestBodyObservable(File file) { this.file = file; ignoreFirstNumberOfWriteToCalls =0; } public ProgressRequestBodyObservable(File file, int ignoreFirstNumberOfWriteToCalls) { this.file = file; this.ignoreFirstNumberOfWriteToCalls = ignoreFirstNumberOfWriteToCalls; } PublishSubject<Float> floatPublishSubject = PublishSubject.create(); public Observable<Float> getProgressSubject(){ return floatPublishSubject; } @Override public MediaType contentType() { return MediaType.parse("image/*"); } @Override public long contentLength() throws IOException { return file.length(); } @Override public void writeTo(BufferedSink sink) throws IOException { numWriteToCalls++; float fileLength = file.length(); byte[] buffer = new byte[2048]; FileInputStream in = new FileInputStream(file); float uploaded = 0; try { int read; read = in.read(buffer); float lastProgressPercentUpdate = 0; while (read != -1) { uploaded += read; sink.write(buffer, 0, read); read = in.read(buffer); // when using HttpLoggingInterceptor it calls writeTo and passes data into a local buffer just for logging purposes. // the second call to write to is the progress we actually want to track if (numWriteToCalls > ignoreFirstNumberOfWriteToCalls ) { float progress = (uploaded / fileLength) * 100; //prevent publishing too many updates, which slows upload, by checking if the upload has progressed by at least 1 percent if (progress - lastProgressPercentUpdate > 1 || progress == 100f) { // publish progress floatPublishSubject.onNext(progress); lastProgressPercentUpdate = progress; } } } } finally { in.close(); } } } 

    Para retrofit 2.0.0-beta4 cargar archivos no está implementado correctamente

    Código fuente del analizador por ahora

     @Documented @Target(PARAMETER) @Retention(RUNTIME) public @interface Part { String value(); String encoding() default "binary"; } // ##### okhttp3.Headers headers = okhttp3.Headers.of( "Content-Disposition", "form-data; name=\"" + part.value() + "\"", "Content-Transfer-Encoding", part.encoding()); 

    Y no hay manera de agregar nombre de archivo por anotación

    Así que usamos este hack para insertar el nombre de archivo

    Por ahora la interfaz debe ser

     @Multipart @POST("some/method") Observable<Response<SomeClass>> UpdateUserPhoto( // RxJava @Part("token") RequestBody token, @Part("avatar\"; filename=\"avatar.png") RequestBody photo ); 

    Y después de construir una solicitud que tomamos

     Content-Disposition: form-data; name="avatar"; filename="avatar.png" Content-Transfer-Encoding: binary 

    RequestBody tipo de medio para el archivo (imagen) debe ser

     MediaType MEDIA_TYPE_IMAGE = MediaType.parse("image/*"); 

    O algo más en su opción

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.