17

ユーザーが Twitpic などのサービスにファイルをアップロードできるようにする Android アプリを開発しています。POST アップロードは外部ライブラリなしで行われ、問題なく動作します。私の唯一の問題は、バイトを出力ストリームに書き込んでいる間ではなく、応答を受信したときにすべてのアップロードが行われるため、進行状況を把握できないことです。これが私がすることです:

URL url = new URL(urlString); 
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
conn.setDoInput(true); 
conn.setDoOutput(true); 
conn.setUseCaches(false); 
conn.setRequestMethod("POST"); 
conn.setRequestProperty("Connection", "Keep-Alive"); 
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); 
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());

次にフォームデータを dos に書き込みますが、ここではあまり重要ではありません。その後、ファイル データ自体を書き込みます (送信するデータの InputStream である "in" から読み取ります)。

while ((bytesAvailable = in.available()) > 0)
{ 
 bufferSize = Math.min(bytesAvailable, maxBufferSize);
 byte[] buffer = new byte[bufferSize];
 bytesRead = in.read(buffer, 0, bufferSize);
 dos.write(buffer, 0, bytesRead);
} 

その後、ファイルの終わりを示すマルチパート フォーム データを送信します。次に、ストリームを閉じます。

in.close(); 
dos.flush(); 
dos.close(); 

これはすべて完全に正常に機能し、これまでのところ問題はありません。ただし、私の問題は、ファイルがどれほど大きくても、ここまでのプロセス全体に約 1 ~ 2 秒かかることです。応答を読むと、アップロード自体が行われているようです。

DataInputStream inStream = new DataInputStream(conn.getInputStream());

ファイルの大きさやインターネット接続の速さによって、数秒から数分かかります。私の質問は次のとおりです。1)バイトを「dos」に書き込んだときにアップロードが発生しないのはなぜですか?2) アップロード中に進行状況ダイアログを表示するには、進行状況を取得するにはどうすればよいですか?

/編集: 1) 問題を少し変更するヘッダーに Content-Length を設定しましたが、決して解決しません: 最後のバイトがストリームに書き込まれた後、コンテンツ全体がアップロードされます。というわけで、データが一気に書き込まれるので、進行状況を把握できないという状況は変わりません。2) Apache HttpClient v4 で MultipartEntity を試しました。リクエストを実行するとすべてのデータが書き込まれるため、OutputStream はまったくありません。繰り返しますが、進歩をつかむ方法はありません。

マルチパート/フォームアップロードでプロセスを取得する方法を他に考えている人はいますか?

4

4 に答える 4

20

ここ数日でかなりのことを試しましたが、最初の質問に対する答えがあると思います。

Androidにはバグ/異常な動作があるため、HttpURLConnectionを使用して進行状況を取得することはできません: http://code.google.com/p/android/issues/detail?id=3164#c6

Froyo の固定ポストになりますが、これは待つことができるものではありません...

したがって、私が見つけた他の唯一のオプションは、Apache HttpClient を使用することです。papleu によってリンクされた回答は正しいですが、一般的な Java を参照しています。そこで使用されるクラスは、Android では使用できなくなりました (HttpClient 3.1 は、かつて SDK の一部でした)。したがって、できることは、HttpClient 4 (具体的には apache-mime4j-0.6.jar および httpmime-4.0.1.jar) からライブラリを追加し、最初の回答 (Tuler による) と最後の回答 (Hamy による) の組み合わせを使用することです。 ):

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;

public class CountingMultipartEntity extends MultipartEntity {

    private final ProgressListener listener;

    public CountingMultipartEntity(final ProgressListener listener) {
        super();
        this.listener = listener;
    }

    public CountingMultipartEntity(final HttpMultipartMode mode, final ProgressListener listener) {
        super(mode);
        this.listener = listener;
    }

    public CountingMultipartEntity(HttpMultipartMode mode, final String boundary,
            final Charset charset, final ProgressListener listener) {
        super(mode, boundary, charset);
        this.listener = listener;
    }

    @Override
    public void writeTo(final OutputStream outstream) throws IOException {
        super.writeTo(new CountingOutputStream(outstream, 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);
        }
    }
}

これは HttpClient 4 で動作しますが、私の apk のサイズが 235 kb になり (質問で説明したマルチパート アップロードを使用したときは 90 kb でした)、さらに悪いことに、インストールされたアプリのサイズが 735 kb (約前に 170 kb)。それは本当にひどいです。アップロード中に進行状況を取得するためだけに、アプリのサイズは以前の 4 倍以上になりました。

于 2010-07-14T12:29:26.163 に答える
0

代わりに WatchedInputStream を使用してください。素晴らしく、使いやすく、素晴らしいです。

  1. WatchedInputStream を使用して入力ストリームをラップします。
  2. watchStream.setListener

    watchStream.setListener(新しいWatchedInputStream.Listener() {

    public void notify(int read) { // UI を更新するために何かを行います。
    } }

于 2012-03-28T07:24:35.150 に答える
0

DefaultHttpClient から進行状況を取得することができます。列に並んでいるという事実

response = defaultHttpClient.execute( httpput, context );

完全なデータが送信されるまで、必ずしも進行状況を把握できないわけではありません。つまり、この行の実行中に HttpContext が変化するため、別の Thread 経由でアプローチすると、その変化が見られる場合があります。必要なのは ConnAdapter です。HttpConnectionMetrics が埋め込まれています

final AbstractClientConnAdapter connAdapter = (AbstractClientConnAdapter) context.getAttribute(ExecutionContext.HTTP_CONNECTION);
final HttpConnectionMetrics metrics = connAdapter.getMetrics();
int bytesSent = (int)metrics.getSentBytesCount();

これを定期的にチェックすると、進行状況が観察されます。スレッドを正しく処理し、すべての try/catch が保護されていることを確認してください。特に、Put のキャンセルや完了時に発生する Context が有効でなくなった場合 (IllegalStateException) に対処する。

于 2012-03-05T08:47:08.943 に答える