web-dev-qa-db-ja.com

Javaを使用したファイルのアップロード(進行状況バーあり))

私はJavaに非常に新しいで、ほとんどの場合、自分で学習しているだけなので、アプレットの作成を開始しました。ローカルディスクからファイルを選択してmultipart/form-dataとしてアップロードできるファイルを作成したいと思いますPOST request but with a progress bar。明らかに、ユーザーはJavaアプレットにハードドライブにアクセスするためのアクセス許可を付与する必要があります。これで、最初の部分は既に機能しています。ユーザーはJFileChooserオブジェクトを使用してファイルを選択できます。 Fileオブジェクトを返します。しかし、次に何が来るのかと思います。File.length()によってファイルの合計サイズがバイト単位で示されることはわかっていますが、選択したFileをWebに送信する方法と方法送信されたバイト数を監視する方法を教えてください。

30
soapergem

結局、オープンソースを偶然見つけてしまいましたJavaアップローダーアプレットで、コード内に必要なすべての情報が見つかりました。これとソースを説明するブログ投稿へのリンクは次のとおりです。

記事
ソースコード

6
soapergem

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を実装します。
プログレスバーの更新は、イベントディスパッチスレッドで実行しないでください。

39
tuler

より単純な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));
    }
}
12
ankostis

リスナーによって返されるバイトの量は、元のファイルサイズとは異なります。したがって、_transferred++_を使用する代わりに、_transferred=len_になるように変更しました。これは、出力ストリームに書き込まれる実際のバイト数の長さです。転送された合計バイト数の合計を計算すると、CountingMultiPartEntity.this.getContentLength();によって返される実際のContentLengthと等しくなります

_public void write(byte[] b, int off, int len) throws IOException {
    wrappedOutputStream_.write(b,off,len);
    transferred=len;
    listener_.transferred(transferred);
}
_
5
douggynix

記事 が役立つかもしれません。 HttpClientとFileUploadの両方を使用して詳細に説明します。どちらもApacheプロジェクトが目的のことを実行します。また、コードサンプルも含まれています。

4

ネットワークの中間コンポーネント(ISPのHTTPプロキシ、またはサーバーの前にあるリバースHTTPプロキシなど)がサーバーよりも速くアップロードを消費する場合、進行状況バーが誤解を招く可能性があることに注意してください。

4
Alexander

Vincentが投稿した記事で述べたように、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();
    }
}
2
el_eduardo

ちょうど私の2cの価値:

これはチューラーの回答に基づいています(執筆時にバグがあります)。少し修正したので、ここに私のバージョンのtulerとmmyersの回答を示します(回答を編集できないようです)。私はこれをもう少しクリーンで高速にしたいと思っていました。バグ(回答のコメントで説明します)の他に、バージョンに関する大きな問題は、書き込みごとに新しいCountingOutputStreamが作成されることです。これは、メモリの面で非常に高価になる可能性があります-大量の割り当てとガベージコレクション。小さな問題は、MultipartEntityを展開するだけのときにデリゲートを使用することです。なぜ彼らがそれを選んだのかわからないので、私はより親しみやすい方法でそれを行いました。誰かが2つのアプローチの長所/短所を知っているなら、それは素晴らしいでしょう。最後に、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);
        }
    }
}
2
Hamy

ファイルをWebにアップロードするために HTTPクライアント を調べます。それができるはずです。進行状況バーを取得する方法はわかりませんが、どういうわけかそのAPIをクエリする必要があります。

1
Rontologist

Apache commonは非常に優れたオプションです。 Apache commonでは、次のものを構成できます。

  1. 最大ファイルサイズ/アップロードファイルサイズを構成(xmlファイル)
  2. 宛先パス(アップロードされたファイルを保存する場所)
  3. 温度を設定します。ファイルを入れ替えるためのフォルダ。ファイルのアップロードが高速になります。
1
Tony

他の回答から、クラスを作成しない場合は、使用している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]
            }
        });
    }

};
1
user3502626