[英]How to get a progress bar for a file upload with Apache HttpClient 4?
我有以下代碼用於使用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)設計的,而Apache HTTP Client 4中不存在RequestEntity類。;(
也許有人有辦法嗎?
許多問候
班尼
我介紹了一個派生的FileEntity
, FileEntity
計算寫入的字節數。 它使用OutputStreamProgress
進行實際計數(實際OutputStream
的裝飾器類型)。
這樣做的好處 (和一般的飾)是,我並不需要從文件流復制實際執行中,如實際拷貝到輸出流。 我也可以改為使用不同的(更新的)實現,比如NFileEntity
。
請享用...
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);
}
}
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;
}
}
使用commons-io(2.4)及其CountingOutputStream類的org.apache.commons.io.output包的新版本。
我更改了初始代碼以反映我的項目需要使用多部分表單作為輸入和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);
}
這個答案通過向OutputStreamProgress.java類添加一個簡單的監聽器來擴展kilaka的答案,而不是使用公共的getProgress()方法(老實說,我不確定如何調用getProgress()方法,因為線程將在內部執行httpclient代碼的整個時間你可能想調用getProgress()!)。
請注意,您需要為要使用的每個實體類型擴展實體類,並且在編寫HttpClient代碼時,您需要創建該新類型的實體。
我寫了一個非常基本的寫偵聽器,它實現了WriteListener接口。 這是您將添加邏輯以使用OutputStreamProgress中的寫入報告執行某些操作的地方,例如更新進度條:)
非常感謝kilaka使用裝飾器的想法潛入計數流程。
public interface WriteListener {
void registerWrite(long amountOfBytesWritten);
}
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();
}
}
public class BasicWriteListener implements WriteListener {
public BasicWriteListener() {
// TODO Auto-generated constructor stub
}
public void registerWrite(long amountOfBytesWritten) {
System.out.println(amountOfBytesWritten);
}
}
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);
// }
}
大家好!
我自己解決了這個問題,並為此做了一個簡單的例子。
如果有任何問題,請隨時提出。
開始了!
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.");
}
}
快樂的編碼!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.