简体   繁体   English

使用 Java 上传文件(带进度条)

[英]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: 以下是描述它的博客文章的链接以及来源:

Article 文章
Source Code 源代码

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

You might find this article helpful. 您可能会发现本文很有帮助。 It explains in detail using HttpClient and FileUpload, both apache projects to do what you want. 它详细解释了使用HttpClient和FileUpload,两个apache项目都可以做你想要的。 It also includes code samples. 它还包括代码示例。

请记住,当网络中的中间组件(例如,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允许您配置以下内容。

  1. Configure(xml file) the maximum file size/ upload file size 配置(xml文件)最大文件大小/上传文件大小
  2. Destination path (where to save the uploaded file) 目标路径(保存上传文件的位置)
  3. Set the temp. 设定温度。 folder to swap the file , so that file upload would be fast. 用于交换文件的文件夹,以便文件上传速度很快。

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

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