我对Java非常新,并且大部分时间都在教我自己,所以我开始构建一个applet。我想创建一个可以从本地磁盘中选择文件并将其作为multipart / form-data POST请求上传但是带有进度条的 。显然,用户必须授予Java applet访问硬盘驱动器的权限。现在我已经完成了第一部分工作:用户可以使用JFileChooser
对象选择一个文件,方便地返回一个File
对象。但我想知道接下来会发生什么。我知道File.length()
将给出文件的总大小(以字节为单位),但是如何将选定的File
发送到Web,以及如何监控已发送的字节数?提前谢谢。
答案 0 :(得分:39)
要使用HttpClient检查进度,请将MultipartRequestEntity包装在计算正在发送的字节数的周围。包装在下面:
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.httpclient.methods.RequestEntity;
public class CountingMultipartRequestEntity implements RequestEntity {
private final RequestEntity delegate;
private final ProgressListener listener;
public CountingMultipartRequestEntity(final RequestEntity entity,
final ProgressListener listener) {
super();
this.delegate = entity;
this.listener = listener;
}
public long getContentLength() {
return this.delegate.getContentLength();
}
public String getContentType() {
return this.delegate.getContentType();
}
public boolean isRepeatable() {
return this.delegate.isRepeatable();
}
public void writeRequest(final OutputStream out) throws IOException {
this.delegate.writeRequest(new CountingOutputStream(out, 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);
}
}
}
然后实现一个更新进度条的ProgressListener 请记住,进度条更新不得在事件调度线程上运行。
答案 1 :(得分:12)
更简单的countEntity不依赖于特定的实体类型,而是扩展HttpEntityWrapped
:
package gr.phaistos.android.util;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.http.HttpEntity;
import org.apache.http.entity.HttpEntityWrapper;
public class CountingHttpEntity extends HttpEntityWrapper {
public static interface ProgressListener {
void transferred(long transferedBytes);
}
static class CountingOutputStream extends FilterOutputStream {
private final ProgressListener listener;
private long transferred;
CountingOutputStream(final OutputStream out, final ProgressListener listener) {
super(out);
this.listener = listener;
this.transferred = 0;
}
@Override
public void write(final byte[] b, final int off, final int len) throws IOException {
//// NO, double-counting, as super.write(byte[], int, int) delegates to write(int).
//super.write(b, off, len);
out.write(b, off, len);
this.transferred += len;
this.listener.transferred(this.transferred);
}
@Override
public void write(final int b) throws IOException {
out.write(b);
this.transferred++;
this.listener.transferred(this.transferred);
}
}
private final ProgressListener listener;
public CountingHttpEntity(final HttpEntity entity, final ProgressListener listener) {
super(entity);
this.listener = listener;
}
@Override
public void writeTo(final OutputStream out) throws IOException {
this.wrappedEntity.writeTo(out instanceof CountingOutputStream? out: new CountingOutputStream(out, this.listener));
}
}
答案 2 :(得分:6)
我最终绊倒了一个开源的Java上传器小程序,并在其代码中找到了我需要知道的一切。以下是描述它的博客文章的链接以及来源:
答案 3 :(得分:5)
侦听器返回的字节数与原始文件大小不同。因此,我修改了transferred++
,而不是transferred=len
。这是写入输出流的实际字节数的长度。当我计算添加的总字节数时,它等于ContentLength
返回的实际CountingMultiPartEntity.this.getContentLength();
public void write(byte[] b, int off, int len) throws IOException {
wrappedOutputStream_.write(b,off,len);
transferred=len;
listener_.transferred(transferred);
}
答案 4 :(得分:4)
您可能会发现此article有用。它详细解释了使用HttpClient和FileUpload,两个apache项目都可以做你想要的。它还包括代码示例。
答案 5 :(得分:4)
请记住,当网络中的中间组件(例如ISP的HTTP代理或服务器前面的反向HTTP代理)比服务器更快地消耗上载时,进度条可能会产生误导。
答案 6 :(得分:2)
正如文森特发表的文章所指出的那样,你可以使用Apache公共文件来做到这一点。
小破了
DiskFileUpload upload = new DiskFileUpload();
upload.setHeaderEncoding(ConsoleConstants.UTF8_ENCODING);
upload.setSizeMax(1000000);
upload.setSizeThreshold(1000000);
Iterator it = upload.parseRequest((HttpServletRequest) request).iterator();
FileItem item;
while(it.hasNext()){
item = (FileItem) it.next();
if (item.getFieldName("UPLOAD FIELD"){
String fileName = item.getString(ConsoleConstants.UTF8_ENCODING);
byte[] fileBytes = item.get();
}
}
答案 7 :(得分:2)
只是我的2c价值:
这是基于调谐器的答案(在撰写时有一个错误)。我稍微修改了一下,所以这是我的调谐器版本和mmyers的答案(我似乎无法编辑他们的答案)。我想尝试让它更清洁,更快。除了这个bug(我在回答他们的评论中讨论)之外,我对他们的版本的重大问题是每次写入都会创建一个新的CountingOutputStream
。就内存而言,这可能会非常昂贵 - 大量的分配和垃圾收集。较小的问题是,当它可以扩展MultipartEntity
时使用委托。不知道他们为什么选择那个,所以我以我更熟悉的方式做到了。如果有人知道两种方法的优点/缺点会很棒。最后,FilterOutputStream#write(byte [],int,int)方法只是在循环中调用FilterOutputStream#write(byte)。 FOS documentation建议子类重写此行为并使其更有效。这里最好的方法是让底层的OutputStream处理写入请求。
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
public class CountingMultiPartEntity extends MultipartEntity {
private UploadProgressListener listener_;
private CountingOutputStream outputStream_;
private OutputStream lastOutputStream_;
// the parameter is the same as the ProgressListener class in tuler's answer
public CountingMultiPartEntity(UploadProgressListener listener) {
super(HttpMultipartMode.BROWSER_COMPATIBLE);
listener_ = listener;
}
@Override
public void writeTo(OutputStream out) throws IOException {
// If we have yet to create the CountingOutputStream, or the
// OutputStream being passed in is different from the OutputStream used
// to create the current CountingOutputStream
if ((lastOutputStream_ == null) || (lastOutputStream_ != out)) {
lastOutputStream_ = out;
outputStream_ = new CountingOutputStream(out);
}
super.writeTo(outputStream_);
}
private class CountingOutputStream extends FilterOutputStream {
private long transferred = 0;
private OutputStream wrappedOutputStream_;
public CountingOutputStream(final OutputStream out) {
super(out);
wrappedOutputStream_ = out;
}
public void write(byte[] b, int off, int len) throws IOException {
wrappedOutputStream_.write(b,off,len);
++transferred;
listener_.transferred(transferred);
}
public void write(int b) throws IOException {
super.write(b);
}
}
}
答案 8 :(得分:1)
查看HTTP Client以将文件上传到网络。它应该能够做到这一点。我不确定如何获得进度条,但它将涉及以某种方式查询该API。
答案 9 :(得分:1)
Apache常见是非常好的选择。 Apache common允许您配置以下内容。
答案 10 :(得分:1)
从其他答案中,您可以覆盖AbstractHttpEntity
类子项或您正在使用的实现public void writeTo(OutputStream outstream)
方法,如果您不想创建类。
使用FileEntity
实例的示例:
FileEntity fileEntity = new FileEntity(new File("img.jpg")){
@Override
public void writeTo(OutputStream outstream) throws IOException {
super.writeTo(new BufferedOutputStream(outstream){
int writedBytes = 0;
@Override
public synchronized void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
writedBytes+=len;
System.out.println("wrote: "+writedBytes+"/"+getContentLength()); //Or anything you want [using other threads]
}
});
}
};