私はJavaに非常に新しいで、ほとんどの場合、自分で学習しているだけなので、アプレットの作成を開始しました。ローカルディスクからファイルを選択してmultipart/form-dataとしてアップロードできるファイルを作成したいと思いますPOST request but with a progress bar。明らかに、ユーザーは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コモンズを使用してこれを行うことができます。
少し切り取られた
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);
}
}
}
ファイルをWebにアップロードするために HTTPクライアント を調べます。それができるはずです。進行状況バーを取得する方法はわかりませんが、どういうわけかそのAPIをクエリする必要があります。
Apache commonは非常に優れたオプションです。 Apache commonでは、次のものを構成できます。
他の回答から、クラスを作成しない場合は、使用している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]
}
});
}
};