简体   繁体   English

如何使用Apache HttpClient 4获取文件上传的进度条?

[英]How to get a progress bar for a file upload with Apache HttpClient 4?

I've got the following code for a file upload with Apache's HTTP-Client (org.apache.http.client): 我有以下代码用于使用Apache的HTTP-Client(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);
    }
  }

It work's quite well but now I want to have a progress bar which shows the progress of the file upload. 它工作得很好,但现在我想有一个进度条,显示文件上传的进度。 How can this be made? 怎么做到这一点? I found a code snippet at File Upload with Java (with progress bar) but it is designed for Apache HTTP Client 3 (org.apache.commons.httpclient) and the RequestEntity class does not exist in Apache HTTP Client 4. ;( 我在File Upload with Java(带进度条)中找到了一个代码片段,但是它是为Apache HTTP Client 3(org.apache.commons.httpclient)设计的,而Apache HTTP Client 4中不存在RequestEntity类。;(

Maybe someone of you has an approach? 也许有人有办法吗?

Many greetings 许多问候

Benny 班尼

I introduced a derived FileEntity that just counts the written bytes. 我介绍了一个派生的FileEntityFileEntity计算写入的字节数。 It uses OutputStreamProgress that does the actual counting (kind of a decorator to the actual OutputStream ). 它使用OutputStreamProgress进行实际计数(实际OutputStream装饰器类型)。

The advantage of this (and decoration in general) is that I do not need to copy the actual implementation, like the the actual copying from the file stream to the output stream. 这样做的好处 (和一般的饰)是,我并不需要从文件流复制实际执行中,如实际拷贝到输出流。 I can also change to use a different (newer) implementation, like the NFileEntity . 我也可以改为使用不同的(更新的)实现,比如NFileEntity

Enjoy... 请享用...

FileEntity.java 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 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;
    }
}

A new version using the package org.apache.commons.io.output from commons-io (2.4) and its class CountingOutputStream . 使用commons-io(2.4)及其CountingOutputStream类的org.apache.commons.io.output包的新版本。

I changed the initial code to reflect my project needs to use a multipart form as input and the post method (this dues to the requirements imposed by the server side). 我更改了初始代码以反映我的项目需要使用多部分表单作为输入和post方法(这取决于服务器端强加的要求)。

Consider that the delta of large file correspond in my tests to 4096 bytes. 考虑到大文件的增量在我的测试中对应于4096字节。 This means that the listener method counterChanged() is called every 4096 bytes of transfered data, what is acceptable for my use case. 这意味着每个4096字节的传输数据都会调用侦听器方法counterChanged(),这对我的用例来说是可以接受的。

The method looks like: 该方法如下:

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

The class MyFileBody becomes: 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;
    }

}

Finally, the listener interface looks like: 最后,监听器界面如下所示:

public interface IStreamListener {

    void counterChanged(int delta);

}

This answer extends kilaka's answer by adding a simple listener to the OutputStreamProgress.java class instead of having the public getProgress() method (I'm honestly not sure how you are suppose to call the getProgress() method since the thread will be executing inside of httpclient's code the entire time you might want to call getProgress()!). 这个答案通过向OutputStreamProgress.java类添加一个简单的监听器来扩展kilaka的答案,而不是使用公共的getProgress()方法(老实说,我不确定如何调用getProgress()方法,因为线程将在内部执行httpclient代码的整个时间你可能想调用getProgress()!)。

Please note you'll need to extend the entity class for each entity type you want to use, and when you write your HttpClient code, you'll need to create the entity of that new type. 请注意,您需要为要使用的每个实体类型扩展实体类,并且在编写HttpClient代码时,您需要创建该新类型的实体。

I wrote a very basic write listener that implements the WriteListener interface. 我写了一个非常基本的写侦听器,它实现了WriteListener接口。 This is where you'll add your logic to do something with the write reports from the OutputStreamProgress, something like updating a progress bar :) 这是您将添加逻辑以使用OutputStreamProgress中的写入报告执行某些操作的地方,例如更新进度条:)

Big thanks to kilaka for using the decorator idea to sneak in a counting outstream. 非常感谢kilaka使用装饰器的想法潜入计数流程。

WriteLisener.java WriteLisener.java

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

OutputStreamProgress.java 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 BasicWriteListener

public class BasicWriteListener implements WriteListener {

public BasicWriteListener() {
    // TODO Auto-generated constructor stub
}

public void registerWrite(long amountOfBytesWritten) {
    System.out.println(amountOfBytesWritten);
}

}

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

}

Hello guys! 大家好!

I solved the problem myself and made ​​a simple example to it. 我自己解决了这个问题,并为此做了一个简单的例子。
If there are any questions, feel free to ask. 如果有任何问题,请随时提出。

Here we go! 开始了!

ApplicationView.java 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 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 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.");
  }
}

Happy Coding! 快乐的编码!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM