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

2022-09-01 22:48:27

我有以下代码用于使用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);
    }
  }

它工作得很好,但现在我想有一个进度条,显示文件上传的进度。这是如何做到的?我在File Upload with Java(带进度条)上找到了一个代码片段,但它是为Apache HTTP Client 3(org.apache.commons.httpclient)设计的,RequestEntity类在Apache HTTP Client 4中不存在。;(

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

许多问候

班尼


答案 1

我引入了一个派生的FileEntity,它只计算写入的字节数。它使用输出流进度来执行实际计数(有点像实际的装饰器)。OutputStream

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

享受。。。

文件实体.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);
    }
}

输出流进度.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字节。这意味着侦听器方法 counterChanged() 每传输 4096 个字节就调用一次,这对于我的用例来说是可以接受的。

该方法如下所示:

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

}

推荐