[英]File Upload with Java (with progress bar)
I'm extremely new to Java, and have mostly just been teaching myself as I go, so I've started building an applet.我对 Java非常陌生,而且大部分时间都在自学,所以我开始构建一个小程序。 I'd like to make one that can select a file from the local disk and upload it as a multipart/form-data POST request but with a progress bar .
我想做一个可以从本地磁盘中选择一个文件并将其上传为 multipart/form-data POST 请求但带有进度条的文件。 Obviously the user has to grant permission to the Java applet to access the hard drive.
显然,用户必须授予 Java 小程序访问硬盘驱动器的权限。 Now I've already got the first part working: the user can select a file using a
JFileChooser
object, which conveniently returns a File
object.现在我已经完成了第一部分的工作:用户可以使用
JFileChooser
对象选择一个文件,该对象方便地返回一个File
对象。 But I'm wondering what comes next.但我想知道接下来会发生什么。 I know that
File.length()
will give me the total size in bytes of the file, but how do I send the selected File
to the web, and how do I monitor how many bytes have been sent?我知道
File.length()
将为我提供文件的总大小(以字节为单位),但是如何将所选File
发送到网络,以及如何监控已发送的字节数? Thanks in advance.提前致谢。
To check progress using HttpClient, wrap the MultipartRequestEntity around one that counts the bytes being sent. 要使用HttpClient检查进度,请将MultipartRequestEntity包装在计算发送字节数的周围。 Wrapper is below:
包装如下:
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);
}
}
}
Then implements a ProgressListener which updates a progress bar. 然后实现一个更新进度条的ProgressListener。
Remember that the progress bar update must not run on the Event Dispatch Thread. 请记住,进度条更新不得在Event Dispatch Thread上运行。
A simpler countingEntity would not depend on a specific entity type but rather extend HttpEntityWrapped
: 更简单的countingEntity不依赖于特定的实体类型,而是扩展
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));
}
}
I ended up stumbling across an open source Java uploader applet and found everything I needed to know within its code. 我最终绊倒了一个开源的Java上传器小程序,并在其代码中找到了我需要知道的一切。 Here are links to a blog post describing it as well as the source:
以下是描述它的博客文章的链接以及来源:
The amount of bytes returned by the listener is different from the original file size. 侦听器返回的字节数与原始文件大小不同。 So, instead of having
transferred++
, I modified it so that transferred=len
; 所以,我没有
transferred++
,而是修改了它,以便transferred=len
; that is the length of the actual amount of bytes being written to the output stream. 这是写入输出流的实际字节数的长度。 And when I compute the addition of the total bytes transferred it is equal to the actual
ContentLength
returned by CountingMultiPartEntity.this.getContentLength();
当我计算添加的总字节数时,它等于
CountingMultiPartEntity.this.getContentLength();
返回的实际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);
}
请记住,当网络中的中间组件(例如,ISP的HTTP代理或服务器前面的反向HTTP代理)比服务器更快地消耗您的上载时,进度条可能会产生误导。
As noted by the article Vincent posted, you can use Apache commons to do this. 正如文森特发表的文章所指出的那样,你可以使用Apache公共文件来做到这一点。
Little snipped 小破了
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();
}
}
Just my 2c worth: 只是我的2c价值:
This is based off of tuler's answer(has a bug at time of writing). 这是基于调谐器的答案(在撰写时有一个错误)。 I modified it slightly, so here is my version of tuler and mmyers answer (I can't seem to edit their answer).
我稍微修改了一下,所以这是我的调谐器版本和mmyers的答案(我似乎无法编辑他们的答案)。 I wanted to attempt to make this a bit cleaner and faster.
我想尝试让它更清洁,更快。 Besides the bug(which I discuss in comments on their answer), the big issue I have with their version is that it creates a new
CountingOutputStream
with every write. 除了bug(我在回答的评论中讨论)之外,我对他们的版本的一个大问题是它在每次写入时都会创建一个新的
CountingOutputStream
。 This can get very expensive in terms of memory - tons of allocations and garbage collections. 就内存而言,这可能会非常昂贵 - 大量的分配和垃圾收集。 Smaller issue is that is uses a delegate when it could just expand the
MultipartEntity
. 较小的问题是当它可以扩展
MultipartEntity
时使用委托。 Not sure why they chose that, so I did it in a manner I was more familiar with. 不知道他们为什么选择那个,所以我以我更熟悉的方式做到了。 If anyone knows pros/cons of the two approaches that would be great.
如果有人知道两种方法的优点/缺点会很棒。 Finally, the FilterOutputStream#write(byte[], int,int) method just calls the FilterOutputStream#write(byte) in a loop.
最后,FilterOutputStream#write(byte [],int,int)方法只是在循环中调用FilterOutputStream#write(byte)。 The FOS documentation recommends subclasses overriding this behavior and making this more efficient.
FOS文档建议子类重写此行为并使其更有效。 The best way to do that here is to let the underlying OutputStream handle the writing request.
这里最好的方法是让底层的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);
}
}
}
Look into HTTP Client for uploadign the file to the web. 查看HTTP客户端以将文件上载到Web。 It should be able to to do that.
它应该能够做到这一点。 I am unsure how to get the progress bar, but it would involve querying that API somehow.
我不确定如何获得进度条,但它将涉及以某种方式查询该API。
From the other answers you can just override the AbstractHttpEntity
class children or implementations public void writeTo(OutputStream outstream)
method you are using if do not want to create a class. 从其他答案中,您可以覆盖
AbstractHttpEntity
类子项或实现public void writeTo(OutputStream outstream)
方法,如果不想创建类,则使用它。
An example using a FileEntity
instance: 使用
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]
}
});
}
};
Apache common is very good option. Apache常见的是非常好的选择。 Apache common allows you to configure following things.
Apache common允许您配置以下内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.