1

I have a MultipartPostRequest with a progress counting feature implemented in the getBody() method. So I display the progress in the notification bar. And when the user clicks on the notification showing the progress, it should be cancelled, much like how Facebook implements it.

I tried using Request.cancel() on the actual Request object, and RequestQueue.cancelAll(Object tag) but the request is still not being cancelled.

I read that an ongoing request cannot be cancelled, but in this case it is still just in the getBody() method, so I think (I'm not really sure) it should be cancelled right? As I also logged the return of the isCancelled() method and yes it does return true when the notification is clicked, and the log is called in the method that returns the progress of the upload Request, so that means it is in the getBody() method.

But surprisingly, and frustratingly, the request still pushes through as it still returns a result!

Is there a way around this? Or is there a way how I can implement it like how Facebook does? I'll gladly give a bounty for this when I can.

I am using the class from this answer : https://stackoverflow.com/a/28144660/1744379

Due to some circumstances I can't share my code. But the class is exactly what I used with which I just did some minor adjustments. The placing of the CountingOutputStream is the same as well as the CountingOutputStream and the MultipartPostProgressListener. So the Log that checks the isCancelled method is inside the transferred method which I implemented in a Service

Community
  • 1
  • 1
John Ernest Guadalupe
  • 6,379
  • 11
  • 38
  • 71
  • Hi! Does my answer work for your question? – BNK Jan 05 '16 at 13:32
  • Hi sorry the client abandoned the idea. But no the answer unfortunately did not work. What worked though is that at the end of the getBody() method, I checked if the request is already cancelled through the `isCancelled` method and then I would give an empty `byte[]` to the request thereby letting the server return an error to me and I would let it by silently – John Ernest Guadalupe Jan 05 '16 at 13:46
  • In my sample project, when cancelUpload called, the file stopped uploading in about 3-5 seconds later. Of course, the data uploaded to server still remains there (for example, a 2MB file will be uploaded about 1MB) – BNK Jan 05 '16 at 13:52
  • Yes the problem here though is that the upload is very fast as we have a limit of only 5mb. So about 3-5 seconds is enough to finish the upload – John Ernest Guadalupe Jan 05 '16 at 15:42

1 Answers1

1

IMO, you can refer to the following:

Firstly, variables needed:

public class MainActivity extends AppCompatActivity {
    private Context mContext = this;
    private Uri mFileUri;
    private String mFilePath;
    private ImageView mImagePreview;
    private ProgressBar mProgressBar;
    StringRequest mStringRequest;
    private long mFileLength;
    private MultipartProgressListener multipartProgressListener;
    ...

Let's assume you got the code to select the image file from phone's camera (gallery) folder and successfully got the file path (mFilePath).

private void cancelUpload(){    
    mStringRequest.cancel();
}

private void uploadFile(){
    mProgressBar.setProgress(0);
    String url = "http://yourserver/fileupload";
    MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
    entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

    if (mFilePath != null) {
        File file = new File(mFilePath);
        if (file.exists()) {
            mFileLength = file.length();
            FileBody fileBody = new FileBody(file);
            entityBuilder.addPart("file", fileBody);

            final HttpEntity httpEntity = entityBuilder.build();

            mStringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    mFilePath = "";
                    Log.i("Multipart", response);
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.e("Multipart", error.toString());
                }
            }) {
                @Override
                public String getBodyContentType() {
                    return httpEntity.getContentType().getValue();
                }

                @Override
                public byte[] getBody() throws AuthFailureError {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    try {
                        //httpEntity.writeTo(bos);
                        httpEntity.writeTo(new CountingOutputStream(bos, mFileLength,
                                multipartProgressListener));
                    } catch (IOException e) {
                        Log.e("Multipart", e.toString());
                    }
                    return bos.toByteArray();

                }
            };
            mStringRequest.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 5, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
            VolleySingleton.getInstance(mContext).addToRequestQueue(mStringRequest);
        }
    }
}

The following code came from the link in your question, I have made some updates (please note they are no static anymore)

    public interface MultipartProgressListener {
        void transferred(long transfered, int progress);
    }

    public class CountingOutputStream extends FilterOutputStream {
        private final MultipartProgressListener progListener;
        private long transferred;
        private long fileLength;

        public CountingOutputStream(final OutputStream out, long fileLength,
                                    final MultipartProgressListener listener) {
            super(out);
            this.fileLength = fileLength;
            this.progListener = listener;
            this.transferred = 0;
        }

        public void write(byte[] b, int off, int len) throws IOException {
            Log.i("isCanceled", String.valueOf(mStringRequest.isCanceled()));
            if (!mStringRequest.isCanceled()) {
                out.write(b, off, len);
                if (progListener != null) {
                    this.transferred += len;
                    int prog = (int) (transferred * 100 / fileLength);
                    this.progListener.transferred(this.transferred, prog);
                }
            } else {
                Log.w("Multipart", "Request canceled!");
                out.flush();
                out.close();
            }
        }

        public void write(int b) throws IOException {
            if (!mStringRequest.isCanceled()) {
                out.write(b);
                if (progListener != null) {
                    this.transferred++;
                    int prog = (int) (transferred * 100 / fileLength);
                    this.progListener.transferred(this.transferred, prog);
                }
            } else {
                Log.w("Multipart", "Request canceled!");
                out.flush();
                out.close();
            }
        }
    }

P/S: you should select large-file sizes :)

BNK
  • 23,994
  • 8
  • 77
  • 87