Visual C# 2005でネットワークプログラミング02 レスポンスヘッダーを解析

Visual C#を使ってネットワークプログラミングするシリーズ
本シリーズはC#でGoogleマップ等のWebAPIを使おうというのが目的です。
第2回である今回は、HTTPレスポンスヘッダーからコンテンツ内容を判定してみます。

レスポンスヘッダー見てコンテンツ内容判定ツール

概要

指定したURLからbyte配列取得し(16進ダンプ)、
レスポンスヘッダーからコンテンツ内容判定(してテキスト又は画像なら表示)する。

画面構成

windows

レスポンスヘッダーとは

・サーバからクライアントへ送信される。
・200 OKなどといったステータスコードに続けて送信される。
・ヘッダの後に、ボディ(text/htmlならソースの中身)が送信される。
・Content-Length:からデータのサイズが分かる
・Content-Type: からデータの種類が分かる(今回の判定対象9
・Last-Modified: から最終更新日が分かる(更新チェックに使える)
・Server: からサーバの種類が分かる
・例
Connection: keep-alive
Accept-Ranges: bytes
Content-Length: 1106
Cache-Control: max-age=604800
Content-Type: image/gif
Date: Wed, 30 Dec 2009 10:39:23 GMT
ETag: “b0225453a1e8c71:1ff9”
Last-Modified: Mon, 27 Aug 2007 11:56:38 GMT
Server: Microsoft-IIS/6.0

byte配列の取得

前回と同じ
・今回は16進ダンプ機能を追加。

レスポンスヘッダーの取得

・System.Net名前空間 WebClient.ResponseHeadersプロパティで取得する。

Content-Typeの判定

・WebClient.ResponseHeaders[“Content-Type”]で取得する。
・StringオブジェクトのStartsWith(“image/”)メソッドを利用して、image/で始まるならば画像コンテンツと判定するのが簡単。
・レスポンスヘッダーの例は以下

[テキスト]
text/html、text/plain、text/css、text/xml
[画像]
image/jpeg、image/png、image/gif
[その他]
application/zip、application/pdf

コード

private void button1_Click(object sender, EventArgs e)
{
    /*  表示領域クリア                                  */
    textBoxResponseHeader.Text = "";
    textBoxContentType.Text = "";
    textBox16.Text = "";
    textBoxText.Text = "";
    labelCharSet.Text = ")";
    pictureBox1.Image = null;

    /*  とりあえずbyte配列として取得する                */
    WebClient myClient = new WebClient();
    byte[] bytedata = myClient.DownloadData(textBoxURL.Text);

    /*  ファイルサイズ確認                              */
    int length = bytedata.Length; ;
    if (length >= 1024 * 1024)
    {
        length = 1024 * 1024; /*  とりあえず1Mbyte      */
    }
    progressBar1.Minimum = 0;
    progressBar1.Maximum = length - 1;

    /*  16進ダンプ                                      */
    {
        int i = 0, j = 0;
        String tmp16 = "";      /*  16進表示部分            */
        String tmpChar = "";    /*  文字列表示部分          */
        String tmpLine = "";    /*  1行分の文字列           */
        for (i = 0; i < length; i += 1)
        {
            /*  1行分の文字列                               */
            if (i == 0)
            {
                tmpLine = "         " + "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F rn" +
                          "---------" + "------------------------------------------------" + "----------------" + "rn";
            }
            else if (i % 16 == 0)
            {
                tmpLine += String.Format("{0:X08} ", i - 16) + tmp16 + tmpChar + "rn";
                tmp16 = "";
                tmpChar = "";
            }
            /*  16進表示部分                                */
            tmp16 += String.Format("{0:X2} ", bytedata[i]);

            /*  文字列表示部分                              */
            if ((bytedata[i] <= 0x19) || (bytedata[i] >= 0x7f))
            {
                /*  制御文字は文字列表示を崩すので.にしとく */
                tmpChar += ".";
            }
            else
            {
                tmpChar += ((char)bytedata[i]).ToString();
            }
            progressBar1.Value = i;
        }
        /*  最終行は16byte単位に満たない分空白詰めで縦を揃える */
        for (j = 0; j < 16 - i % 16; j++)
        {
            tmp16 += "   ";
        }
        tmpLine += String.Format("{0:X08} ", i - i % 16) + tmp16 + tmpChar + "rn";
        textBox16.Text += tmpLine;
        progressBar1.Value = 0;
    }
    /*  Content-Typeを判定する                              */
    {
        /*  レスポンスヘッダーの取得                        */
        textBoxResponseHeader.Text += myClient.ResponseHeaders;
        textBoxContentType.Text = myClient.ResponseHeaders["Content-Type"];

        /*  Content-Typeの判定                              */
        String type = myClient.ResponseHeaders["Content-Type"];
        if (type.StartsWith("image/"))
        {
            /*  画像ならばストリームとして取得して表示      */
            Stream httpstream = myClient.OpenRead(textBoxURL.Text);
            pictureBox1.Image = Image.FromStream(httpstream);
            httpstream.Close();
        }
        else if (type.StartsWith("text/"))
        {
            /*  テキストならテキストとして取得して表示      */
            /*  文字エンコーディング方式を判定              */
            myClient.Encoding = myDetectEncoding(bytedata);
            textBoxText.Text = myClient.DownloadString(textBoxURL.Text);
        }
    }
}
private Encoding myDetectEncoding(byte[] data)
{
    /*  バイト配列をASCIIエンコードで文字列に変換       */
    String s = Encoding.ASCII.GetString(data);
    /*  <meta>タグを抽出するための正規表現              */
    Match mymatch = Regex.Match(s,
    @"<metas+[^>]*charsets*=s*([-_w]+)",
    RegexOptions.IgnoreCase);
    String e;
    if(mymatch.Success)
    {
        e = mymatch.Groups[1].Value;
        labelCharSet.Text = Encoding.GetEncoding(e).WebName + ")";
    }
    else
    {
        /*  <meta>タグから分からないのでshift-jisにしとく */
        e = "shift-jis";
        labelCharSet.Text = "不明)";
    }
    return Encoding.GetEncoding(e);
}

memo

今回のツール作成で得たチップスをメモリます。
チップス集として別記事にまとめるかもしれません。

文字列操作

・StringオブジェクトのStartsWithメソッドで先頭部分が調べられる。

ストリームからImageオブジェクト取得

Stream httpstream = myClient.OpenRead(textBoxURL.Text);
pictureBox1.Image = Image.FromStream(httpstream);
httpstream.Close();

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です