【问题标题】:How to get a progress bar for a file upload with Apache HttpClient 4?如何使用 Apache HttpClient 4 获取文件上传的进度条?
【发布时间】:2011-10-26 19:20:52
【问题描述】:

我有以下代码用于使用 Apache 的 HTTP 客户端 (org.apache.http.client) 上传文件:

  public static void main(String[] args) throws Exception
  {
    String fileName = "test.avi";
    File file = new File(fileName);

    String serverResponse = null;
    HttpParams params = new BasicHttpParams();
    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, true);
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpClient client = new DefaultHttpClient(params);
    HttpPut put = new HttpPut("http://localhost:8080/" + fileName);

    FileEntity fileEntity = new FileEntity(file, "binary/octet-stream");
    put.setEntity(fileEntity);   

    HttpResponse response = client.execute(put);
    HttpEntity entity = response.getEntity();
    if (entity != null)
    {
      serverResponse = EntityUtils.toString(entity);
      System.out.println(serverResponse);
    }
  }

它工作得很好,但现在我想要一个显示文件上传进度的进度条。这怎么能做?我在File Upload with Java (with progress bar) 找到了一个代码 sn-p,但它是为 Apache HTTP Client 3 (org.apache.commons.httpclient) 设计的,并且 RequestEntity 类在 Apache HTTP Client 4 中不存在。;(

也许你们中的某个人有办法?

许多问候

本尼

【问题讨论】:

  • 有没有办法获得上传的字节数?请添加一个代码sn-p,我可能会有你的答案
  • 感谢您的帮助!我自己找到了解决办法,会及时介绍。 :)
  • 您可能还喜欢:stackoverflow.com/questions/22932821/… 它具有包装解决方案,对您的类层次结构的干扰较小。

标签: java apache http client progress


【解决方案1】:

我介绍了一个派生的 FileEntity,它只计算写入的字节数。 它使用 OutputStreamProgress 来进行实际计数(类似于实际 OutputStream装饰器)。

这个(和一般装饰)的优点是我确实不需要需要复制实际的实现,就像从文件流实际复制到输出流。我还可以更改为使用不同的(更新的)实现,例如 NFileEntity

享受...

FileEntity.java

public class FileEntity extends org.apache.http.entity.FileEntity {

    private OutputStreamProgress outstream;

    public FileEntity(File file, String contentType) {
        super(file, contentType);
    }

    @Override
    public void writeTo(OutputStream outstream) throws IOException {
        this.outstream = new OutputStreamProgress(outstream);
        super.writeTo(this.outstream);
    }

    /**
     * Progress: 0-100
     */
    public int getProgress() {
        if (outstream == null) {
            return 0;
        }
        long contentLength = getContentLength();
        if (contentLength <= 0) { // Prevent division by zero and negative values
            return 0;
        }
        long writtenLength = outstream.getWrittenLength();
        return (int) (100*writtenLength/contentLength);
    }
}

OutputStreamProgress.java

public class OutputStreamProgress extends OutputStream {

    private final OutputStream outstream;
    private volatile long bytesWritten=0;

    public OutputStreamProgress(OutputStream outstream) {
        this.outstream = outstream;
    }

    @Override
    public void write(int b) throws IOException {
        outstream.write(b);
        bytesWritten++;
    }

    @Override
    public void write(byte[] b) throws IOException {
        outstream.write(b);
        bytesWritten += b.length;
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        outstream.write(b, off, len);
        bytesWritten += len;
    }

    @Override
    public void flush() throws IOException {
        outstream.flush();
    }

    @Override
    public void close() throws IOException {
        outstream.close();
    }

    public long getWrittenLength() {
        return bytesWritten;
    }
}

【讨论】:

  • 喜欢装饰者的想法。我在另一个答案中对其进行了扩展。
  • 你在哪里扩展它?也许它在这里很有价值。
  • 糟糕!我的意思是投票赞成。我在你的下方回复了这个帖子。
  • 这不只是监控文件写入输出流的进度,而不是实际上传的进度吗?如果我理解正确,则在刷新输出流之前不会发生实际上传。
【解决方案2】:

使用 commons-io (2.4) 中的包 org.apache.commons.io.output 及其类 CountingOutputStream 的新版本.

我更改了初始代码以反映我的项目需要使用多部分表单作为输入和 post 方法(这是由于服务器端施加的要求)。

考虑到我的测试中大文件的增量对应于 4096 字节。这意味着每传输 4096 个字节的数据就会调用侦听器方法 counterChanged(),这对于我的用例来说是可以接受的。

方法如下:

public void post(String url, File sendFile) {
    HttpParams params = new BasicHttpParams();
    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, true);
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpClient client = new DefaultHttpClient(params);
    HttpPost post = new HttpPost(url + "/" + sendFile.getName());
    MultipartEntity multiEntity = new MultipartEntity(); 
    MyFileBody fileBody = new MyFileBody(sendFile);

    fileBody.setListener(new IStreamListener(){

        @Override
        public void counterChanged(int delta) {
            // do something
            System.out.println(delta);
        }});

    multiEntity.addPart("file", fileBody);
    StringBody stringBody = new StringBody(sendFile.getName());
    multiEntity.addPart("fileName", stringBody);
    post.setEntity(multiEntity);   
    HttpResponse response = client.execute(post);
}

MyFileBody 类变成:

public class MyFileBody extends FileBody {

    private IStreamListener listener;

    public MyFileBody(File file) {
        super(file);
    }

    @Override
    public void writeTo(OutputStream out) throws IOException {
        CountingOutputStream output = new CountingOutputStream(out) {
            @Override
            protected void beforeWrite(int n) {
                if (listener != null && n != 0)
                    listener.counterChanged(n);
                super.beforeWrite(n);
            }
        };
        super.writeTo(output);

    }

    public void setListener(IStreamListener listener) {
        this.listener = listener;
    }

    public IStreamListener getListener() {
        return listener;
    }

}

最后,监听器界面如下:

public interface IStreamListener {

    void counterChanged(int delta);

}

【讨论】:

    【解决方案3】:

    这个答案通过向 OutputStreamProgress.java 类添加一个简单的侦听器而不是公共 getProgress() 方法来扩展 kilaka 的答案(老实说,我不确定你应该如何调用 getProgress() 方法,因为线程将在您可能想要调用 getProgress() 的整个过程中一直在 httpclient 的代码中执行!)。

    请注意,您需要为要使用的每种实体类型扩展实体类,并且在编写 HttpClient 代码时,您需要创建该新类型的实体。

    我编写了一个非常基本的写监听器,它实现了 WriteListener 接口。在这里,您将添加逻辑以对来自 OutputStreamProgress 的写入报告执行某些操作,例如更新进度条 :)

    非常感谢 kilaka 使用装饰器的想法潜入计数外流。

    WriteLisener.java

    public interface WriteListener {
        void registerWrite(long amountOfBytesWritten);
    }
    

    OutputStreamProgress.java

    import java.io.IOException;
    import java.io.OutputStream;
    
    public class OutputStreamProgress extends OutputStream {
    
        private final OutputStream outstream;
        private long bytesWritten=0;
        private final WriteListener writeListener;
        public OutputStreamProgress(OutputStream outstream, WriteListener writeListener) {
            this.outstream = outstream;
            this.writeListener = writeListener;
        }
    
        @Override
        public void write(int b) throws IOException {
            outstream.write(b);
            bytesWritten++;
            writeListener.registerWrite(bytesWritten);
        }
    
        @Override
        public void write(byte[] b) throws IOException {
            outstream.write(b);
            bytesWritten += b.length;
            writeListener.registerWrite(bytesWritten);
        }
    
        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            outstream.write(b, off, len);
            bytesWritten += len;
            writeListener.registerWrite(bytesWritten);
        }
    
        @Override
        public void flush() throws IOException {
            outstream.flush();
        }
    
        @Override
        public void close() throws IOException {
            outstream.close();
        }
    }
    

    BasicWriteListener

    public class BasicWriteListener implements WriteListener {
    
    public BasicWriteListener() {
        // TODO Auto-generated constructor stub
    }
    
    public void registerWrite(long amountOfBytesWritten) {
        System.out.println(amountOfBytesWritten);
    }
    
    }
    

    MultipartEntityWithProgressBar

    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 MultipartEntityWithProgressBar extends MultipartEntity {
        private OutputStreamProgress outstream;
        private WriteListener writeListener;
    
        @Override
        public void writeTo(OutputStream outstream) throws IOException {
            this.outstream = new OutputStreamProgress(outstream, writeListener);
            super.writeTo(this.outstream);
        }
    
        public MultipartEntityWithProgressBar(WriteListener writeListener)
        {
            super();
            this.writeListener = writeListener;
        }
        public MultipartEntityWithProgressBar(HttpMultipartMode mode, WriteListener writeListener)
        {
            super(mode);
            this.writeListener = writeListener;
        }
        public MultipartEntityWithProgressBar(HttpMultipartMode mode, String boundary, Charset charset, WriteListener writeListener)
        {
            super(mode, boundary, charset);
            this.writeListener = writeListener;
        }
    
        // Left in for clarity to show where I took from kilaka's answer
    //  /**
    //   * Progress: 0-100
    //   */
    //  public int getProgress() {
    //      if (outstream == null) {
    //          return 0;
    //      }
    //      long contentLength = getContentLength();
    //      if (contentLength <= 0) { // Prevent division by zero and negative values
    //          return 0;
    //      }
    //      long writtenLength = outstream.getWrittenLength();
    //      return (int) (100*writtenLength/contentLength);
    //  }
    
    }
    

    【讨论】:

    • 在 OutputStreamProgress 中,您能解释一下为什么 bytesWritten 变量被声明为 volatile 吗?这个article 说它在与 '++' 和 '+=' 一起使用时可能不是线程安全的,在这种情况下这是一个潜在的错误吗?谢谢
    • @bmeding 我不确定 volatile 关键字是如何进入其中的!我很久以前写过这门课,但我已经编辑过从我的答案中删除它,我认为它根本没有必要。
    【解决方案4】:

    大家好!

    我自己解决了这个问题,并做了一个简单的例子。
    如果有任何问题,请随时提出。

    我们开始吧!

    ApplicationView.java

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.File;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JProgressBar;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.HttpVersion;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpPut;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.params.BasicHttpParams;
    import org.apache.http.params.HttpParams;
    import org.apache.http.params.HttpProtocolParams;
    import org.apache.http.util.EntityUtils;
    
    public class ApplicationView implements ActionListener
    {
    
      File file = new File("C:/Temp/my-upload.avi");
      JProgressBar progressBar = null;
    
      public ApplicationView()
      {
        super();
      }
    
      public void createView()
      {
        JFrame frame = new JFrame("File Upload with progress bar - Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(0, 0, 300, 200);
        frame.setVisible(true);
    
        progressBar = new JProgressBar(0, 100);
        progressBar.setBounds(20, 20, 200, 30);
        progressBar.setStringPainted(true);
        progressBar.setVisible(true);
    
        JButton button = new JButton("upload");
        button.setBounds(progressBar.getX(),
                progressBar.getY() + progressBar.getHeight() + 20,
                100,
                40);
        button.addActionListener(this);
    
        JPanel panel = (JPanel) frame.getContentPane();
        panel.setLayout(null);
        panel.add(progressBar);
        panel.add(button);
        panel.setVisible(true);
      }
    
      public void actionPerformed(ActionEvent e)
      {
        try
        {
          sendFile(this.file, this.progressBar);
        }
        catch (Exception ex)
        {
          System.out.println(ex.getLocalizedMessage());
        }
      }
    
      private void sendFile(File file, JProgressBar progressBar) throws Exception
      {
        String serverResponse = null;
        HttpParams params = new BasicHttpParams();
        params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, true);
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpClient client = new DefaultHttpClient(params);
        HttpPut put = new HttpPut("http://localhost:8080/" + file.getName());
    
        ProgressBarListener listener = new ProgressBarListener(progressBar);
        FileEntityWithProgressBar fileEntity = new FileEntityWithProgressBar(file, "binary/octet-stream", listener);
        put.setEntity(fileEntity);
    
        HttpResponse response = client.execute(put);
        HttpEntity entity = response.getEntity();
        if (entity != null)
        {
          serverResponse = EntityUtils.toString(entity);
          System.out.println(serverResponse);
        }
      }
    }
    

    FileEntityWithProgressBar.java

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import org.apache.http.entity.AbstractHttpEntity;
    
    /**
     * File entity which supports a progress bar.<br/>
     * Based on "org.apache.http.entity.FileEntity".
     * @author Benny Neugebauer (www.bennyn.de)
     */
    public class FileEntityWithProgressBar extends AbstractHttpEntity implements Cloneable
    {
    
      protected final File file;
      private final ProgressBarListener listener;
      private long transferredBytes;
    
      public FileEntityWithProgressBar(final File file, final String contentType, ProgressBarListener listener)
      {
        super();
        if (file == null)
        {
          throw new IllegalArgumentException("File may not be null");
        }
        this.file = file;
        this.listener = listener;
        this.transferredBytes = 0;
        setContentType(contentType);
      }
    
      public boolean isRepeatable()
      {
        return true;
      }
    
      public long getContentLength()
      {
        return this.file.length();
      }
    
      public InputStream getContent() throws IOException
      {
        return new FileInputStream(this.file);
      }
    
      public void writeTo(final OutputStream outstream) throws IOException
      {
        if (outstream == null)
        {
          throw new IllegalArgumentException("Output stream may not be null");
        }
        InputStream instream = new FileInputStream(this.file);
        try
        {
          byte[] tmp = new byte[4096];
          int l;
          while ((l = instream.read(tmp)) != -1)
          {
            outstream.write(tmp, 0, l);
            this.transferredBytes += l;
            this.listener.updateTransferred(this.transferredBytes);
          }
          outstream.flush();
        }
        finally
        {
          instream.close();
        }
      }
    
      public boolean isStreaming()
      {
        return false;
      }
    
      @Override
      public Object clone() throws CloneNotSupportedException
      {
        return super.clone();
      }
    }
    

    ProgressBarListener.java

    import javax.swing.JProgressBar;
    
    public class ProgressBarListener
    {
    
      private int transferedMegaBytes = 0;
      private JProgressBar progressBar = null;
    
      public ProgressBarListener()
      {
        super();
      }
    
      public ProgressBarListener(JProgressBar progressBar)
      {
        this();
        this.progressBar = progressBar;
      }
    
      public void updateTransferred(long transferedBytes)
      {
        transferedMegaBytes = (int) (transferedBytes / 1048576);
        this.progressBar.setValue(transferedMegaBytes);
        this.progressBar.paint(progressBar.getGraphics());
        System.out.println("Transferred: " + transferedMegaBytes + " Megabytes.");
      }
    }
    

    编码愉快!

    【讨论】:

    • 这属于 NetBeans 为我所做的自动生成。 :)
    • 为什么进度条的完成时间是实际上传时间的三分之一?
    猜你喜欢
    • 2014-02-03
    • 2017-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2011-02-01
    相关资源
    最近更新 更多