Javaでのファイルアップロード(プログレスバー付き)
-
05-07-2019 - |
質問
私はJavaを非常に新しく しているので、ほとんどの場合は自分で教えているので、アプレットの作成を始めました。ローカルディスクからファイルを選択し、multipart / form-data POSTリクエストとしてアップロードできるものを作成したいが、進行状況バー付き。明らかに、ユーザーはJavaアプレットにハードドライブにアクセスする許可を与える必要があります。これで、最初の部分が機能するようになりました。ユーザーは JFileChooser
オブジェクトを使用してファイルを選択できます。これにより、 File
オブジェクトが便利に返されます。しかし、次に何が来るのだろうかと思っています。 File.length()
がファイルの合計サイズをバイト単位で提供することは知っていますが、選択した File
をWebに送信するにはどうすればよいですか。送信されたバイト数を監視しますか?事前に感謝します。
他のヒント
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を実装します。
プログレスバーの更新は、イベントディスパッチスレッドで実行しないでください。
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));
}
}
リスナーから返されるバイト数は、元のファイルサイズと異なります。そこで、 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);
}
この記事が役立つ場合があります。 HttpClientとFileUploadを使用して詳細に説明します。どちらも、必要なことを行うためのApacheプロジェクトです。コードサンプルも含まれています。
ネットワーク内の中間コンポーネント(ISPのHTTPプロキシ、サーバーの前のリバースHTTPプロキシなど)がサーバーよりも速くアップロードを消費する場合、プログレスバーが誤解を招く可能性があることに注意してください。
Vincentが投稿した記事で指摘されているように、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();
}
}
ちょうど私の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);
}
}
}
HTTPクライアントを見て、ファイルをWebにアップロードします。それができるはずです。進行状況バーを取得する方法はわかりませんが、そのAPIを何らかの方法で照会する必要があります。
Apache commonは非常に良いオプションです。 Apache commonでは、次のものを設定できます。
- 最大ファイルサイズ/アップロードファイルサイズを構成(xmlファイル)
- 宛先パス(アップロードされたファイルを保存する場所)
- 温度を設定しますファイルをスワップするフォルダ。ファイルのアップロードが高速になります。
他の回答から、クラスを作成しない場合は、使用している 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]
}
});
}
};