2

I watched the HttpClient progress code but I still have questions I couldn't find answer Where to get this ProgressListener to put the constructor parameter? And how to use the code correctly? Please help

Here is the code

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;

public class CountingMultipartEntity extends MultipartEntity {

    private final ProgressListener listener;

    public CountingMultipartEntity(final ProgressListener listener) {
        super();
        this.listener = listener;
    }

    public CountingMultipartEntity(final HttpMultipartMode mode, final ProgressListener listener) {
        super(mode);
        this.listener = listener;
    }

    public CountingMultipartEntity(HttpMultipartMode mode, final String boundary,
            final Charset charset, final ProgressListener listener) {
        super(mode, boundary, charset);
        this.listener = listener;
    }

    @Override
    public void writeTo(final OutputStream outstream) throws IOException {
        super.writeTo(new CountingOutputStream(outstream, this.listener));
    }

    public static interface ProgressListener {
        void transferred(long num);
    }

    public static class CountingOutputStream extends FilterOutputStream {

        private final ProgressListener listener;
        private long transferred;

        public CountingOutputStream(final OutputStream out,
                final ProgressListener listener) {
            super(out);
            this.listener = listener;
            this.transferred = 0;
        }


        public void write(byte[] b, int off, int len) throws IOException {
            out.write(b, off, len);
            this.transferred += len;
            this.listener.transferred(this.transferred);
        }

        public void write(int b) throws IOException {
            out.write(b);
            this.transferred++;
            this.listener.transferred(this.transferred);
        }
    }
}

Can I implement the interface like this?

...
public static interface ProgressListener {
        void transferred(long num);
    }
    public static class Progress implements ProgressListener
    {

      public void transferred(long num) {
//            // update the progress bar or whatever else you might want to do

        }

    }
...

But how can I init the ProgressListener as for this outer class which contains HttpClient then?

CountingMultiPartEntity entity = new CountingMultiPartEntity(new ProgressListener() {

        public void transferred(long num) {
            // update the progress bar or whatever else you might want to do
        }
    });
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
user592704
  • 3,674
  • 11
  • 70
  • 107

3 Answers3

2

You'll need to construct an implementation of the ProgressListener interface yourself if you don't have any classes that implement it already. For things like listeners, this is commonly done with an anonymous inner class.

CountingMultipartEntity entity = new CountingMultipartEntity(new ProgressListener() {
    @Override
    public void transferred(long num) {
        // update the progress bar or whatever else you might want to do
    }
});
Paul Blessing
  • 3,815
  • 2
  • 24
  • 25
  • I wondering what lib the ProgressListener is? Because I get fileNotFound :( I have HttpClient 4 – user592704 Mar 16 '11 at 03:28
  • And what HttpMultipartMode mode param means as for HttpClient? – user592704 Mar 16 '11 at 03:34
  • Quote: "You'll need to construct an implementation of the ProgressListener interface yourself if you don't have any classes that implement it already. For things like listeners, this is commonly done with an anonymous inner class." I don't have the interface implementation :( I've never seen any examples in this field. Can you show me? – user592704 Mar 16 '11 at 03:46
  • Please show me how to construct ProgressListener implementation because I am confused with the code style :( The ProgressListener is an inner interface so can I just make it an outer? I don't get it... – user592704 Mar 16 '11 at 04:05
  • You already have the definition of the `ProgressListener` interface in the code you posted. You can implement it using a normal standalone class, a named inner class, or an anonymous inner class. There are a bunch of articles around about using these, see any article about adding event listeners to Swing components. One example is at http://download.oracle.com/javase/tutorial/uiswing/events/generalrules.html#innerClasses – Paul Blessing Mar 17 '11 at 02:05
0

You should import the ProgressListener to your class where you are going to call

import com.blahblah.YourProjectName.CustomMultipartEntity.ProgressListener;

I'm using a similar class named "CustomMultipartEntity", don't forget to change it's name.

The CustomMultipartEntity class implementation is:

package com.blahblah.YourProjectName;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
public class CustomMultipartEntity extends MultipartEntity
{


private final ProgressListener listener;

public CustomMultipartEntity(final ProgressListener listener)
{
    super();
    this.listener = listener;
}

public CustomMultipartEntity(final HttpMultipartMode mode, final ProgressListener listener)
{
    super(mode);
    this.listener = listener;
}

public CustomMultipartEntity(HttpMultipartMode mode, final String boundary, final Charset charset, final ProgressListener listener)
{
    super(mode, boundary, charset);
    this.listener = listener;
}

@Override
public void writeTo(final OutputStream outstream) throws IOException
{
    super.writeTo(new CountingOutputStream(outstream, this.listener));
}

public static interface ProgressListener
{
    void transferred(long num);
}

public static class CountingOutputStream extends FilterOutputStream
{

    private final ProgressListener listener;
    private long transferred;

    public CountingOutputStream(final OutputStream out, final ProgressListener listener)
    {
        super(out);
        this.listener = listener;
        this.transferred = 0;
    }

    public void write(byte[] b, int off, int len) throws IOException
    {
        out.write(b, off, len);
        this.transferred += len;
        this.listener.transferred(this.transferred);
    }

    public void write(int b) throws IOException
    {
        out.write(b);
        this.transferred++;
        this.listener.transferred(this.transferred);
    }
}
}

And this is an example call:

CustomMultipartEntity reqEntity = new CustomMultipartEntity(new ProgressListener() {

                    public void transferred(long num) {
                        if((int) ((num / (float) totalSize) * 100) > 0)
                            progressHandler(num);
                    }
                });
Oğuz Sezer
  • 320
  • 3
  • 15
0

I found this here: https://www.baeldung.com/httpclient-post-http-request#get-file-upload-progress

The idea is that after you (normally) create your entity, you wrap it into another entity, while specifying a listener object, which will get notifications whenever bytes are transmitted to the remote host. In the basic example, this notification merely contains a percentage (bytes transmitted / bytes total * 100), but you can expand this to suit your needs.

For completeness, here's the code listed at the above link:

You start with an interface:

public interface ProgressListener {
    void progress(float percentage);
}

Next you have the entity wrapper:

import org.apache.http.HttpEntity;
import org.apache.http.entity.HttpEntityWrapper;

public class ProgressEntityWrapper extends HttpEntityWrapper {
    private ProgressListener listener;

    public ProgressEntityWrapper(HttpEntity entity, ProgressListener listener) {
        super(entity);
        this.listener = listener;
    }

    @Override
    public void writeTo(OutputStream outstream) throws IOException {
        super.writeTo(new CountingOutputStream(outstream, listener, getContentLength()));
    }
}

Finally you have the OutputStream, which tracks progress:

public class CountingOutputStream extends FilterOutputStream {
    private ProgressListener listener;
    private long transferred;
    private long totalBytes;

    public CountingOutputStream(
      OutputStream out, ProgressListener listener, long totalBytes) {
        super(out);
        this.listener = listener;
        transferred = 0;
        this.totalBytes = totalBytes;
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        out.write(b, off, len);
        transferred += len;
        listener.progress(getCurrentProgress());
    }

    @Override
    public void write(int b) throws IOException {
        out.write(b);
        transferred++;
        listener.progress(getCurrentProgress());
    }

    private float getCurrentProgress() {
        return ((float) transferred / totalBytes) * 100;
    }
}

Disclaimer: The above is simply copy/paste from the aforementioned link. (while adding some imports and removing static modifiers)

This worked well for me. Be sure to specify a Content-Length header in your response, or else the value of totalBytes will be -1, which will bork the calculation.

Pro tip: you can probably optimize things a bit, by sending fewer notifications at the cost of precision. I'd try something like this:

public CounterOutputStream(OutputStream out, ProgressListener listener, long totalBytes) {
    super(out);
    final ProgressListener xl = listener;
    this.listener = new ProgressListener() {
        private int p = 0;

        @Override
        public void progress(float percentage) {
            if (Math.floor(percentage) > p) {
                p = (int) percentage;
                xl.progress(percentage);
            }
        }
    };
    transferred = 0;
    this.totalBytes = totalBytes;
}

Another disclaimer: I haven't actually tested the above new version of the constructor for CounterOutputStream.

You use all of this like so:

HttpEntity entity = MultipartEntityBuilder.create()
  .setMode(HttpMultipartMode.RFC6532)
  .addTextBody("Part1", "Some text", Constants.CT_U8_TEXT)
  .addTextBody("Part2", moreText, Constants.CT_U8_TEXT)
  .addTextBody("Part3", someVariable, Constants.CT_U8_TEXT)
  .build()
;

// This instruction here:
entity = new ProgressEntityWrapper(
  entity
  , percentage -> LOG.info("Upload at: " + percentage)
);

HttpUriRequest post = RequestBuilder
  .post(url)
  .setEntity(entity)
  .build()
;

// etc...
Dharman
  • 30,962
  • 25
  • 85
  • 135
DotMatrix
  • 31
  • 1
  • 2
  • 4