プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

【Android】マルチパートのHTTP POSTのやり方

androidでフォームからファイルをアップロードするように、マルチパートのデータをPOSTで送信する場合は、若干工夫が必要です。

ここでは、『型番リーダー』を作成した際に、WeOCRサーバーに対して各種設定と画像ファイルをマルチパートのHTTP POSTで送信した際の方法をまとめておきます。

なお、以下では大まかな処理の説明だけをしているので、変数・定数定義などは特にふれていません(ある程度名前で判断がつくと思います)。必要があれば、型番リーダーのソースコードをダウンロードしてご覧いただければさいわいです。

 

1.型番リーダーのやり方(マルチパートデータを自分で作成する方法)

javaの標準のクラスを使い、自分でマルチパートデータを構成して送信する方法です。

(1)最初に接続先のURLを作成し、接続オブジェクトを取得します。

 

            URL url = new URL(mServerUrl + mServerCgi);
             if (useProxy) {
                url = new URL(HTTP_PROT, PRXYHOST, PRXYPORT, mServerUrl + mServerCgi);
            }

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

ここでは、フラグuseProxyがtrueの場合にはプロキシサーバー経由になる処理を加えていますが、本質的ではありませんので削除しても問題はありません(デバッグ時に、通信内容を見るために追加してあります)。

 

(2)次に、セットアップパラメータと一般要求プロパティを設定します。

 

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);

            // ヘッダ設定
            conn.setRequestMethod(POST_METHOD);

            conn.setRequestProperty(CONNECTION, KEEP_ALIVE);
            conn.setRequestProperty(CHARSET, UTF8);
            conn.setRequestProperty(CONTENT_TYPE, MULTIPART_BOUNDARY + mBoundary);

 

(3)次に、送信データの出力先となるストリームを取得し、

 

            DataOutputStream os = new DataOutputStream(conn.getOutputStream());

 

(4)これに対して、データを出力します。

 

            append_string(os, FORM_LBL_OUTENC, UTF8.toLowerCase());
            append_string(os, FORM_LBL_OUTFORMAT, FORM_VAL_FORMAT);
            append_string(os, FORM_LBL_ELEMENTCLASS, FORM_VAL_ELEMENTCLASS);
            
            Log.d(TAG, "image length: " + image.length);
            
            append_pgmfile(os, FORM_LBL_USRFILE, FORM_VAL_FILE, image, width, height);
            append_final(os);

 

ここで、appnd_stringは、フォームの変数名と変数値をマルチパートの形式で出力する関数です。ここでは、次のように定義しています。

 

    private void append_string(DataOutputStream os, String name, String value) throws IOException {
        os.writeBytes(twohyphens + mBoundary + end);
        os.writeBytes("Content-Disposition: form-data; name=" + dq + name + dq + end + end);
        os.writeBytes(value+end);
    }

 

また、append_pgmfileは、PGM形式のバイナリデータをPOSTメソッドで送るためのメソッドです。ここでは、次のように定義しています。

 

    private void  append_pgmfile(DataOutputStream os, String name, String filename, byte [] value, 
         int width, int height) throws IOException {
         os.writeBytes(twohyphens + mBoundary + end);
         os.writeBytes("Content-Disposition: form-data; name=" + dq + name + dq +
                         "; filename=" + dq + filename + dq + end);
         os.writeBytes("Content-Type: application/octet-stream" + end + end);
 
         // PGMファイルのストリームへの書き出し
         Pgm.write(value, width, height, os);
         
         os.writeBytes(end);        
     }

 

Pgm.write()は、与えられたストリームに対して、 PGM形式のバイナリデータを書き込む処理をまとめたものです。Pgm.writeに該当する処理を、出力したいファイルのバイト列を書き出す処理に置き換えれば、どのようなファイルにも使えると思います。

append_finalは、boundaryを出力し、最後にハイフンを二つ追加して、マルチパートデータの終わりを示すための関数です。

 

    private void append_final(DataOutputStream os) throws IOException {
        os.writeBytes(twohyphens + mBoundary + twohyphens + end);
    }

 

(5)結果を受信します。

まず、レスポンスコードを確認し、コードが 200 (OK)以外の場合は例外を投げるようにしています。

 

            // 結果受け取り
            final int responceCode = conn.getResponseCode();
            Log.d(TAG, "responceCode: " + responceCode);
            
            if (responceCode != HttpURLConnection.HTTP_OK) {
                throw new RuntimeException("invalid responce code, " + responceCode);
            }

レスポンスコードに対応する処理は必要に応じて変更すればよいと思います。

 

(6)受信結果を処理します。

レスポンスコードがOKであれば、受信結果を処理します。なお、受信データの入力ストリームは、conn.getInputStreamにより取得したものを使用します。

 

            // 受信データ処理
            parseResult(conn.getInputStream());
            
            Log.d(TAG, "ocr result (between brackets): [" + mResult + "]");
            
            conn.disconnect();

 

実際の処理は、parseResultで行っています。ここでは、WeOCRサーバーへのアクセスで何か異常が発生した場合は、先頭行が空行以外の文字列が入る、ということを利用した、エラー判定を加えています。

 

    private void parseResult(InputStream is) throws IOException {    
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        
        // 先頭行が空行以外の場合は、エラー
        boolean error_responce = false;
        String s = "";
        String out = "";

        // 先頭行
        if (null == (s = br.readLine())) {
            error_responce = true;
        } else {
            if (0 != s.length()) {
                error_responce = true;
                out = s;
            }
        }

        // 2行目以降
        while(null != (s = br.readLine())) {
            if (0 == out.length()) {
                out += s;
            } else {
                out += "n" + s;
            }
        }

        if (error_responce) {
            throw new RuntimeException("failuer of analyze: " + out);
        }

        mResult = out;
    }

これで、一連のPOST処理と結果の受け取りができると思います。

 

 

(参考サイト)

HTTP POST (multipart/form-data) on Android(英語)

httpclient multipart form upload(英語)

Multipart form upload on Android(英語)

URLConnectionを使い、POST型で、CGIを呼び出すには

Useful Code of the Day: Multipart Form File Upload(英語)

 

2.ライブラリ追加する方法

今回は試さなかったのですが、下記のサイトに紹介されているように、ライブラリを追加して、そのクラスを利用することで簡単にマルチパートデータのPOST送信を行うこともできるようです。

私の方では試してないので詳しくはリンク先をご覧ください。

"Andoridでmultipart/form-dataのPOST"の続き

 

3.参考:デバッグについて

自分でマルチパートの送信データを作成すると、デバッグが難しくなると思います。その際、下記の記事を参考に通信内容を取得してWebデバッグを行いました。ご参考にしてください。

AndroidでWebマッシュアップ開発の基礎