続・Java でストリーム
keisuken さんのコメントにある方法をやろうかと思ったけど、手間なので違う方法を試してみた。
やりたいことは、HTTP リクエストに対するバイナリデータを含んだ応答を適切に取得すること。
こんなコードを書いたら、うまく取得できるようになった。
InputStream in = socket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(in);
bis.mark(8192);
String headers = getHeaders(bis);
bis.reset();
bis.skip(getHeaderSize(headers));
int len = getContentLength(headers);
DataInputStream dis = new DataInputStream(bis);
byte buff = getBinaryData(dis, len);
:
そいで、getHeaders() 内では、きちんと BufferedReader を使ってヘッダ部分の文字列を読み込むように作ってある。
String[] getHeaders(BufferedInputStream in) { BufferedReader reader = new BufferedReader(new InputStreamRaeder(in)); ArrayListlist = new ArrayList (); while (true) { String s = reader.readLine(); if (s == null || s.length() == 0) { break; } list.add(s); } return list.toArray(new String[list.size()]); }
ポイントは、BufferedInputStream の mark()/reset()/skip() を使っているところ。DataInputStream の開始ポイントは、ヘッダの最後の方にセットされるけど、getHeaders() で取得した文字列には CR+LF を含まないので、バイナリデータよりもその分だけ手前に。
ちなみに、ひとつのヘッダに対して文字列長+2(CF+LFの分)を加えてヘッダサイズを計算すると、正しい値が求められそうなんだけど、世の中には CR だけで改行している HTTP サーバがいたりするので、その場合には正しく読めなくなってしまう。
getBinaryData() では、ストリームの先頭から連続した CR+LF(または連続した CR) が見つかるまでを読み捨てて、その後をバイナリデータとみなすようにしてみた。