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デバッグを行いました。ご参考にしてください。