私はJava に非常に慣れておらず、ほぼ独学で勉強していたので、アプレットの作成を開始しました。ローカル ディスクからファイルを選択し、それを multipart/form-data POST リクエストとしてアップロードできますが、プログレス バーを使用できるものを作成したいと思います。明らかに、ユーザーは Java アプレットにハード ドライブへのアクセスを許可する必要があります。これで、最初の部分は既に機能しています。ユーザーはオブジェクトを使用してファイルを選択できます。JFileChooser
これにより、便利なようにオブジェクトが返されFile
ます。しかし、私は次がどうなるか疑問に思っています。File.length()
ファイルの合計サイズがバイト単位で表示されることはわかっていますが、選択File
したファイルを Web に送信するにはどうすればよいですか? また、送信されたバイト数を監視するにはどうすればよいですか? 前もって感謝します。
10 に答える
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
。これは、出力ストリームに書き込まれる実際のバイト数の長さです。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);
}
ネットワークの中間コンポーネント (例: ISP の HTTP プロキシ、またはサーバーの前にあるリバース HTTP プロキシ) がアップロードをサーバーよりも速く消費する場合、プログレス バーは誤解を招く可能性があることに注意してください。
ちょうど私の2cの価値:
これはチューラーの回答に基づいています(執筆時点でバグがあります)。私はそれを少し変更したので、ここに私のバージョンのチューラーと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);
}
}
}
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();
}
}
Apache common は非常に良いオプションです。Apache common では、次の設定を行うことができます。
- 構成 (xml ファイル) 最大ファイル サイズ/アップロード ファイル サイズ
- 宛先パス (アップロードされたファイルの保存先)
- 温度を設定します。ファイルのアップロードが高速になるように、ファイルをスワップするフォルダー。
ファイルを Web にアップロードするには、HTTP クライアントを調べます。それができるはずです。プログレスバーを取得する方法はわかりませんが、何らかの方法でその API を照会する必要があります。
他の回答から、クラスを作成したくない場合は、使用している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]
}
});
}
};