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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/254719/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 12:04:33  来源:igfitidea点击:

File Upload with Java (with progress bar)

javafile-iouploadprogress-bar

提问by soapergem

I'm extremely newto Java, and have mostly just been teaching myself as I go, so I've started building an applet. 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. Obviously the user has to grant permission to the Java applet to access the hard drive. Now I've already got the first part working: the user can select a file using a JFileChooserobject, which conveniently returns a Fileobject. 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 Fileto the web, and how do I monitor how many bytes have been sent? Thanks in advance.

我对 Java非常陌生,而且大部分时间都在自学,所以我开始构建一个小程序。我想做一个可以从本地磁盘中选择一个文件并将其上传为 multipart/form-data POST 请求但带有进度条的文件。显然,用户必须授予 Java 小程序访问硬盘驱动器的权限。现在我已经完成了第一部分的工作:用户可以使用一个JFileChooser对象选择一个文件,它可以方便地返回一个File对象。但我想知道接下来会发生什么。我知道这File.length()会给我文件的总大小(以字节为单位),但是如何将所选文件发送File到网络,以及如何监控已发送的字节数?提前致谢。

采纳答案by soapergem

I ended up stumbling across an open source Java uploader applet and found everything I needed to know within its code. Here are links to a blog post describing it as well as the source:

我最终偶然发现了一个开源 Java 上传器小程序,并在它的代码中找到了我需要知道的一切。以下是描述它的博客文章以及来源的链接:

Article
Source Code

文章
源代码

回答by Rontologist

Look into HTTP Clientfor uploadign the file to the 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.

查看HTTP 客户端以将文件上传到网络。它应该能够做到这一点。我不确定如何获取进度条,但它会涉及以某种方式查询该 API。

回答by Vincent Ramdhanie

You might find this articlehelpful. It explains in detail using HttpClient and FileUpload, both apache projects to do what you want. It also includes code samples.

您可能会发现这篇文章很有帮助。它详细解释了使用 HttpClient 和 FileUpload 这两个 apache 项目来做你想做的事。它还包括代码示例。

回答by el_eduardo

As noted by the article Vincent posted, you can use Apache commons to do this.

正如 Vincent 发表的文章所述,您可以使用 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();
    }
}

回答by Alexander

Keep in mind that the progress bar might be misleading when an intermediate component in the network (e.g., an ISP's HTTP proxy, or a reverse HTTP proxy in front of the server) consumes your upload faster than the server does.

请记住,当网络中的中间组件(例如,ISP 的 HTTP 代理或服务器前的反向 HTTP 代理)消耗您的上传速度比服务器快时,进度条可能会产生误导。

回答by tuler

To check progress using HttpClient, wrap the MultipartRequestEntity around one that counts the bytes being sent. Wrapper is below:

要使用 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);
        }
    }
}

Then implements a ProgressListener which updates a progress bar.
Remember that the progress bar update must not run on the Event Dispatch Thread.

然后实现一个 ProgressListener 来更新进度条。
请记住,进度条更新不得在事件调度线程上运行。

回答by Tony

Apache common is very good option. Apache common allows you to configure following things.

Apache common 是非常好的选择。Apache common 允许您配置以下内容。

  1. Configure(xml file) the maximum file size/ upload file size
  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.
  1. 配置(xml文件)最大文件大小/上传文件大小
  2. 目标路径(上传文件的保存位置)
  3. 设置温度。文件夹来交换文件,这样文件上传会很快。

回答by Hamy

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). 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 CountingOutputStreamwith every write. 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. 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. The FOS documentationrecommends subclasses overriding this behavior and making this more efficient. The best way to do that here is to let the underlying OutputStream handle the writing request.

这是基于 tuler 的回答(在撰写本文时有一个错误)。我稍微修改了一下,所以这是我的 tuler 和 mmyers 版本的答案(我似乎无法编辑他们的答案)。我想尝试让它更干净、更快。除了错误(我在他们的答案的评论中讨论过)之外,我对他们的版本的一个大问题是它CountingOutputStream在每次写入时都会创建一个新的。这在内存方面会变得非常昂贵 - 大量的分配和垃圾收集。较小的问题是当它可以扩展MultipartEntity. 不知道他们为什么选择那个,所以我以我更熟悉的方式做了。如果有人知道这两种方法的优缺点,那就太好了。最后,FilterOutputStream#write(byte[], int,int) 方法只是在循环中调用 FilterOutputStream#write(byte)。FOS 文档建议使用子类覆盖此行为并使其更有效。最好的方法是让底层的 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);
        }
    }
}

回答by ankostis

A simpler countingEntity would not depend on a specific entity type but rather extend HttpEntityWrapped:

一个更简单的 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));
    }
}

回答by douggynix

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; 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 ContentLengthreturned by CountingMultiPartEntity.this.getContentLength();

侦听器返回的字节数与原始文件大小不同。所以,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);
}