30

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

4

10 回答 10

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。
请记住,进度条更新不得在事件调度线程上运行。

于 2009-01-22T17:21:06.787 回答
12

更简单的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));
    }
}
于 2011-09-06T11:25:30.870 回答
6

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

文章
源代码

于 2009-07-18T21:46:23.427 回答
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);
}
于 2012-04-26T09:17:42.150 回答
4

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

于 2008-11-01T00:37:17.187 回答
2

正如文森特发布的文章所述,您可以使用 Apache commons 来执行此操作。

小剪


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

于 2008-10-31T21:57:54.807 回答
2

只是我的 2c 价值:

这是基于 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);
        }
    }
}
于 2010-07-01T03:33:37.660 回答
1

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

于 2008-10-31T19:55:43.917 回答
1

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

  1. 配置(xml文件)最大文件大小/上传文件大小
  2. 目标路径(保存上传文件的位置)
  3. 设置温度。文件夹来交换文件,这样文件上传会很快。
于 2009-04-16T12:25:04.590 回答
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]
            }
        });
    }

};
于 2016-04-03T12:13:43.070 回答