Visual C#を使ってネットワークプログラミングするシリーズ
本シリーズはC#でGoogleマップ等のWebAPIを使おうというのが目的です。
第2回である今回は、HTTPレスポンスヘッダーからコンテンツ内容を判定してみます。
レスポンスヘッダー見てコンテンツ内容判定ツール
概要
指定したURLからbyte配列取得し(16進ダンプ)、
レスポンスヘッダーからコンテンツ内容判定(してテキスト又は画像なら表示)する。
画面構成
レスポンスヘッダーとは
・サーバからクライアントへ送信される。
・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();
