今日の役に立たない一言 - Today’s Trifle! -

古い記事ではさまざまなテーマを書いていますが、2007年以降はプログラミング関連の話がほとんどです。

続・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));
        ArrayList list = 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) が見つかるまでを読み捨てて、その後をバイナリデータとみなすようにしてみた。