MultipartEntityBuilder with progressbar

2020-08-03 06:33发布

问题:

I am using the following code to upload a photo to my server with MultipartEntityBuilder. Somewhere at the end it says // FIXME Put your progress bar stuff here!. I don't know if I should put the whole function into an Asynctask or what I should do to display progress while the file is uploaded.

I checked Upload a file through an HTTP form, via MultipartEntityBuilder, with a progress bar but it doesn't really help.

public String postFile(String fileName) throws Exception {

          fileName = "/mnt/sdcard/DCIM/100MEDIA/IMAG0309.jpg";

            HttpClient client = new DefaultHttpClient();
            HttpPost post = new HttpPost("http://www.mywebsite.com/upload_file.php");
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();        
            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);  

            final File file = new File(fileName);
            FileBody fb = new FileBody(file);
            builder.addPart("file",fb);

            String userid = session_userid;
            builder.addTextBody("userID", userid);

            final HttpEntity yourEntity = builder.build();

            class ProgressiveEntity implements HttpEntity {
                @Override
                public void consumeContent() throws IOException {
                    yourEntity.consumeContent();                
                }
                @Override
                public InputStream getContent() throws IOException,
                        IllegalStateException {
                    return yourEntity.getContent();
                }
                @Override
                public Header getContentEncoding() {             
                    return yourEntity.getContentEncoding();
                }
                @Override
                public long getContentLength() {
                    return yourEntity.getContentLength();
                }
                @Override
                public Header getContentType() {
                    return yourEntity.getContentType();
                }
                @Override
                public boolean isChunked() {             
                    return yourEntity.isChunked();
                }
                @Override
                public boolean isRepeatable() {
                    return yourEntity.isRepeatable();
                }
                @Override
                public boolean isStreaming() {             
                    return yourEntity.isStreaming();
                } // CONSIDER put a _real_ delegator into here!

                @Override
                public void writeTo(OutputStream outstream) throws IOException {

                    class ProxyOutputStream extends FilterOutputStream {

                        public ProxyOutputStream(OutputStream proxy) {
                            super(proxy);    
                        }
                        public void write(int idx) throws IOException {
                            out.write(idx);
                        }
                        public void write(byte[] bts) throws IOException {
                            out.write(bts);
                        }
                        public void write(byte[] bts, int st, int end) throws IOException {
                            out.write(bts, st, end);
                        }
                        public void flush() throws IOException {
                            out.flush();
                        }
                        public void close() throws IOException {
                            out.close();
                        }
                    }

                    class ProgressiveOutputStream extends ProxyOutputStream {

                         int totalSent;

                        public ProgressiveOutputStream(OutputStream proxy) {
                            super(proxy);
                        }
                        public void write(byte[] bts, int st, int end) throws IOException {

                            // FIXME  Put your progress bar stuff here!

                            out.write(bts, st, end);
                        }
                    }

                    yourEntity.writeTo(new ProgressiveOutputStream(outstream));
                }

            };
            ProgressiveEntity myEntity = new ProgressiveEntity();

            post.setEntity(myEntity);
            HttpResponse response = client.execute(post);        

                return getContent(response);

   } 

Here is what I did but I still need you guys for finishing it:

 @Override
            protected String doInBackground(HttpResponse... arg0)
            {
              String fileName = "/mnt/sdcard/DCIM/100MEDIA/IMAG0309.jpg";
                HttpClient client = new DefaultHttpClient();
                HttpPost post = new HttpPost("http://www.mywebsite.com/upload_file.php");
                MultipartEntityBuilder builder = MultipartEntityBuilder.create();        
                builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);  

                final File file = new File(fileName);
                FileBody fb = new FileBody(file);
                builder.addPart("file",fb);

                    String userid = session_userid;
                builder.addTextBody("userID", userid);
                final HttpEntity yourEntity = builder.build();

                class ProgressiveEntity implements HttpEntity {
                    @Override
                    public void consumeContent() throws IOException {
                        yourEntity.consumeContent();                
                    }
                    @Override
                    public InputStream getContent() throws IOException,
                            IllegalStateException {
                        return yourEntity.getContent();
                    }
                    @Override
                    public Header getContentEncoding() {             
                        return yourEntity.getContentEncoding();
                    }
                    @Override
                    public long getContentLength() {
                        return yourEntity.getContentLength();
                    }
                    @Override
                    public Header getContentType() {
                        return yourEntity.getContentType();
                    }
                    @Override
                    public boolean isChunked() {             
                        return yourEntity.isChunked();
                    }
                    @Override
                    public boolean isRepeatable() {
                        return yourEntity.isRepeatable();
                    }
                    @Override
                    public boolean isStreaming() {             
                        return yourEntity.isStreaming();
                    } // CONSIDER put a _real_ delegator into here!

                    @Override
                    public void writeTo(OutputStream outstream) throws IOException {

                        class ProxyOutputStream extends FilterOutputStream {

                            public ProxyOutputStream(OutputStream proxy) {
                                super(proxy);    
                            }
                            public void write(int idx) throws IOException {
                                out.write(idx);
                            }
                            public void write(byte[] bts) throws IOException {
                                out.write(bts);
                            }
                            public void write(byte[] bts, int st, int end) throws IOException {
                                out.write(bts, st, end);
                            }
                            public void flush() throws IOException {
                                out.flush();
                            }
                            public void close() throws IOException {
                                out.close();
                            }
                        }

                        class ProgressiveOutputStream extends ProxyOutputStream {
                            public ProgressiveOutputStream(OutputStream proxy) {
                                super(proxy);
                                int totalSent = 0;
                            }
                            public void write(byte[] bts, int st, int end) throws IOException {

                                // FIXME  Put your progress bar stuff here!
                                 //what's totalSize?
                                publishProgress((int) ((end / (float) totalSize) * 100));
                                out.write(bts, st, end);
                            }
                        }

                        yourEntity.writeTo(new ProgressiveOutputStream(outstream));
                    }

                };
                ProgressiveEntity myEntity = new ProgressiveEntity();

                post.setEntity(myEntity);
                HttpResponse response = client.execute(post);        

                    return getContent(response);
//Here it says I should put this into a try catch but then I get an error on
// the doInBackground line saying it needs to return a string

                }

                protected void onProgressUpdate(Integer... progress) {
                    pd.setProgress((int) (progress[0]));
               }

            @SuppressWarnings("deprecation")
            @Override
             protected void onPostExecute(String file_url) {
                pd.dismiss();
                connection.disconnect();
                Toast.makeText(getApplicationContext(), "Ready.", Toast.LENGTH_LONG).show();
            }

        }

回答1:

I was trying to achieve something similar (I use ProgressDialog instead of ProgressBar). You have to use AsyncTask so the main thread won't be waiting for your upload to finish (Upload must be asynchronous).

  1. Create a extension for HttpEntityWrapped (credits: here):

    public class MyHttpEntity extends HttpEntityWrapper {
    
    private ProgressListener progressListener;
    
    public MyHttpEntity(final HttpEntity entity, final ProgressListener progressListener) {
        super(entity);
        this.progressListener = progressListener;
    }
    
    @Override
    public void writeTo(OutputStream outStream) throws IOException {
        this.wrappedEntity.writeTo(outStream instanceof ProgressOutputStream ? outStream :
                new ProgressOutputStream(outStream, this.progressListener,
                        this.getContentLength()));
    }
    
    public static interface ProgressListener {
        void transferred(float progress);
    }
    
    public static class ProgressOutputStream extends FilterOutputStream {
    
        private final ProgressListener progressListener;
    
        private long transferred;
    
        private long total;
    
        public ProgressOutputStream(final OutputStream outputStream,
                                    final ProgressListener progressListener,
                                    long total) {
    
            super(outputStream);
            this.progressListener = progressListener;
            this.transferred = 0;
            this.total = total;
        }
    
        @Override
        public void write(byte[] buffer, int offset, int length) throws IOException {
    
            out.write(buffer, offset, length);
            this.transferred += length;
            this.progressListener.transferred(this._getCurrentProgress());
        }
    
        @Override
        public void write(byte[] buffer) throws IOException {
    
            out.write(buffer);
            this.transferred++;
            this.progressListener.transferred(this._getCurrentProgress());
        }
    
        private float _getCurrentProgress() {
            return ((float) this.transferred / this.total) * 100;
        }
    }
    

    }

  2. I've created private class that extends AsyncTask in an Activity. In doInBackground you use MultipartEntityBuilder to add your file and anything you want. If upload is successful (code == 200, it's my custom json response), onPostExecute will be called.

    private class UploadAsyncTask extends AsyncTask {

    private Context context;
    private Exception exception;
    private ProgressDialog progressDialog;
    
    private UploadAsyncTask(Context context) {
    
        this.context = context;
    }
    
    @Override
    protected Boolean doInBackground(String... params) {
    
        String uploadUrl = URL + "upload";
    
        HttpResponse response = null;
    
        try {
            HttpPost post = new HttpPost(uploadUrl);
    
            // Entity
            MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
            // Add your file
            multipartEntityBuilder.addPart("file", new FileBody(new File(params[0])));
    
            // Progress listener - updates task's progress
            MyHttpEntity.ProgressListener progressListener =
                    new MyHttpEntity.ProgressListener() {
                        @Override
                        public void transferred(float progress) {
                            UploadAsyncTask.this.publishProgress((int) progress);
                        }
                    };
    
            // POST
            post.setEntity(new MyHttpEntity(multipartEntityBuilder.build(),
                    progressListener));
    
            // Handle response
            GsonBuilder builder = new GsonBuilder();
            LinkedTreeMap object = builder.create().fromJson(
                    EntityUtils.toString(client.execute(post).getEntity()),
                    LinkedTreeMap.class);
    
            if ((double) object.get("code") == 200.0) {
                return true;
            }
    
        } catch (UnsupportedEncodingException | JsonSyntaxException | ClientProtocolException e) {
            e.printStackTrace();
            Log.e("UPLOAD", e.getMessage());
            this.exception = e;
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        return false;
    }
    
    @Override
    protected void onPreExecute() {
    
        // Init and show dialog
        this.progressDialog = new ProgressDialog(this.context);
        this.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        this.progressDialog.show();
    }
    
    @Override
    protected void onPostExecute(Boolean result) {
    
        // Close dialog
        this.progressDialog.dismiss();
        if (result) {
            Toast.makeText(getApplicationContext(),
                    R.string.upload_success, Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(getApplicationContext(),
                    R.string.upload_failure, Toast.LENGTH_LONG).show();
        }
    }
    
    @Override
    protected void onProgressUpdate(Integer... progress) {
        // Update process
        this.progressDialog.setProgress((int) progress[0]);
    }
    

    }

  3. Call upload within a Acitvity:

    private void _upload() {

    UploadAsyncTask task = new UploadAsyncTask(this);
    task.execute();
    

    }