Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Friday, February 24, 2017

Is it possible to show progress bar when upload image via Retrofit 2

Is it possible to show progress bar when upload image via Retrofit 2


I'am currently using Retrofit 2 and i want to upload some photo at my server. I know, that older version uses TypedFile class for uploading. And if we want to use progress bar with it we should override writeTo method in TypedFile class.

Is it possible to show progress when using retrofit 2 library?

Answer by Shubham A. for Is it possible to show progress bar when upload image via Retrofit 2


As far as I can see in this post, no updates regarding the image upload progress response has been made and you still have to override the writeTo method as shown in this SO answer by making a ProgressListener interface and using a sub-class of TypedFile to override the writeTo method.

So, there isn't any built-in way to show progress when using retrofit 2 library.

Answer by Yuriy Kolbasinskiy for Is it possible to show progress bar when upload image via Retrofit 2


First of all, you should use Retrofit 2 version equal or above 2.0 beta2. Second, create new class 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));                      }      }  }  

Third, create interface

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

Now you can get progress of your upload.

In your activity (or 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 request = RetrofitClient.uploadImage(filepart);              request.enqueue(new Callback{...});          }            @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);          }          }  

Answer by iscariot for Is it possible to show progress bar when upload image via Retrofit 2


For retrofit 2.0.0-beta4 uploading files is not implemented correctly

parser source code for now

@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());  

and there is no way to add filename by annotation

so we use this hack to insert filename

for now interface must be

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

and after build a request we take

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

RequestBody media type for file(image) must be

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

or something else in your option

Answer by Justin Fiedler for Is it possible to show progress bar when upload image via Retrofit 2


Here's how to handle upload file progress with a simple POST rather than Multipart. For multipart check out @Yariy's solution. Additionally, this solution uses Content URI's instead of direct file references.

RestClient

@Headers({      "Accept: application/json",      "Content-Type: application/octet-stream"  })  @POST("api/v1/upload")  Call 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 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;      }  }  

To initiate the upload:

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

Warning, if you forget to override the contentLength() function you may receive a few obscure errors:

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

Or

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

Or

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

These are a result of RequestBody.writeTo() being called multiple times as the default contentLength() is -1.

Anyways this took a long time to figure out, hope it helps.

Useful links: https://github.com/square/retrofit/issues/1217

Answer by Troy Yuan-Ting Wu for Is it possible to show progress bar when upload image via Retrofit 2


I update progressbar onProgressUpdate. This code can get better performance.

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


Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72

0 comments:

Post a Comment

Popular Posts

Powered by Blogger.