目次

概要

アクセスログ記録に続き、アクセスカウンタを。

せっかくだから色々やってみようということで、 以下のような作り方をします。

  • カウント数は、サーバ上のファイルに書いておきます。

  • カウントは Global.asax の Session_Start で行います。

  • 前回のアクセス時のカウント数を Cookie で記録。リピート状況を見ます。

  • カウント数の表示方法を何パターンか説明(Web フォーム中に表示 & 画像カウンタ)

そのため、以下のような項目について解説。

  • Session State。

  • Cookie の利用。

  • ASP.NET を使った画像カウンタ

カウント処理

アクセスログ記録と同じく、 カウント処理は Global.asax の Session_Start イベントハンドラで行うことにします。

ファイルの読み書きに関しては、 やることはほとんど「ログの記録」と同じです。 カウント数を保存しておくファイルですが、 ここで説明する例では総アクセス数しか記録しませんが、 将来的に「今日だけのアクセス数」とかも記録することを考えて、 count\total.txt というファイルに書くことにします。

例えば、以下のような感じのコードを Global.asax.cs(Global.asax のコードビハインド)のクラス中に追加します。

string GetTotalCount()
{
  string filename = Request.PhysicalApplicationPath +
    @"\count\total.txt";

  Application.Lock();

  int num;
  using (FileStream fs = new FileStream(
    filename, FileMode.OpenOrCreate,
    FileAccess.ReadWrite, FileShare.None))
  {
    StreamReader sr = new StreamReader(fs);
    string line = sr.ReadLine();

    if (string.IsNullOrEmpty(line) || !int.TryParse(line, out num))
    {
      num = 0;
    }

    line = (num + 1).ToString();

    fs.Seek(0, SeekOrigin.Begin);

    StreamWriter sw = new StreamWriter(fs);
    sw.WriteLine(line);
    sw.Flush();
  }
  Application.UnLock();

  return num.ToString();
}

protected void Session_Start(Object sender, EventArgs e)
{
  if (CheckExcludeList())
    return;

  AddAccessLog();

  string count = GetTotalCount();
  Session["TotalCount"] = count;
}

この例中、最後の行の Session["TotalCount"] = count; なんですが、 Session はセッション状態を記憶しておくために使うプロパティです。 (HttpApplication や Page クラスのプロパティで、 HttpSessionState 型。) ここで Session["TotalCount"] に代入した値は、 セッションがタイムアウトするまでずっと保持されます。

Cookie の利用

ここで、カウント数もアクセスログに記録してみることにします。 また、前回のアクセス時のカウント数も記録して、 リピート状況をログに取れるようにします。 前回のアクセスカウント数は、Cookie を使ってクライアント側に記憶しておいてもらいます。

Cookie は、 Request.Cookies でクライアントから送られて来た Cookie を取得し、 Response.Cookies でサーバから送り返す Cookie を設定します。

ということで、 「ログの記録」で作った AddAccessLog メソッドを以下のように書き換えます。

void AddAccessLog(string count)
{
      string basePath = Request.PhysicalApplicationPath + @"\accesslog\";
      DateTime now = DateTime.Now;
      string filename = basePath
        + string.Format("{0}{1:00}.csv", now.Year, now.Month);

  string prev;
  if (Request.Cookies["PREV"] != null)
    prev = Request.Cookies["PREV"].Value;
  else
    prev = count;

  Response.Cookies["PREV"].Value = count;

  Application.Lock();

  using (StreamWriter sw = new StreamWriter(filename, true))
  {
    sw.Write("\"" + DateTime.Now.ToString() + "\",");
    sw.Write("\"" +
      System.Net.Dns.GetHostEntry(Request.UserHostName).HostName +
      "\",");
    sw.Write("\"" + Request.UserAgent + "\",");
    sw.Write("\"" + Request.Url + "\",");
    sw.Write("\"" + Request.UrlReferrer + "\"\n");
  }
  Application.UnLock();
}

protected void Session_Start(Object sender, EventArgs e)
{
  if (CheckExcludeList())
    return;

  string count = GetTotalCount();
  Session["count"] = count;
  AddAccessLog(count);
}

カウント数の表示(Web フォーム中)

Global.asax で Session 状態に記憶したカウント数を表示したいわけですが、 Web フォーム(.aspx)ページ中に表示するのは非常に簡単です。

1番簡単な方法でいうと、カウント数を表示したい位置に以下の1行を書くだけ。

<%= Session["count"] %>

例えば、以下のような感じ。

<%@ Page Language="C#" %>

<html>
<head>
  <title>テストページ</title>
</head>
<body>
  <p>
    総アクセス数: <%= Session["count"] %>
  </p>
</body>
</html>

画像カウンタ

普通の html ページ上にカウンタを設置しすることを考えて、 CGI などでよくやるような、画像カウンタを作ることもできます。

ASP.NET の Web フォームの出力を画像にしたければ、 Response.ClearContent や Response.ContentType などを使って、 Page_Load イベントハンドラに以下のようなコードを書きます。 (画像の作り方に関してはほんの一例。 要点は強調表示している4行。)

private void Page_Load(object sender, System.EventArgs e)
{
  string text = (string)Session["TotalCount"];
  text = text.PadLeft(7, '0');
  Font font = new Font("MS ゴシック", 15);

  Bitmap bitmap = new Bitmap(75, 20);
  Graphics graphics = Graphics.FromImage(bitmap); 

  graphics.FillRectangle(Brushes.White, 0, 0, 75 , 20);
  graphics.DrawString(text, font, Brushes.Black,0,2);

  Response.ClearContent();
  Response.ContentType = "image/gif";
  bitmap.Save(Response.OutputStream, ImageFormat.Gif);
  Response.End();

  graphics.Dispose();
  bitmap.Dispose(); 
}

.aspx ファイル中に何を書いていようと、 Response.ClearContent() メソッドを呼び出すことで、 一度出力結果がまっさらになるので、 .aspx には <script> タグだけを書くか、 何も書かず <%@ Page %> ディレクティブでコードビハインドの設定だけを書きます。

例えば以下のような感じ。 (ここでは、これのファイル名は Counter.aspx としておきます。)

<%@ Page language="c#" %>
<%@ import Namespace="System.Drawing.Imaging" %>
<%@ import Namespace="System.Drawing" %>

<script runat="server">

private void Page_Load(object sender, System.EventArgs e)
{
  string text = (string)Session["count"];
  text = text.PadLeft(7, '0');
  Font font = new Font("MS ゴシック", 15);

  Bitmap bitmap = new Bitmap(75, 20);
  Graphics graphics = Graphics.FromImage(bitmap); 

  graphics.FillRectangle(Brushes.White, 0, 0, 75 , 20);
  graphics.DrawString(text, font, Brushes.Black,0,2);

  Response.ClearContent();
  Response.ContentType = "image/gif";
  bitmap.Save(Response.OutputStream, ImageFormat.Gif);
  Response.End();

  graphics.Dispose();
  bitmap.Dispose(); 
}

</script>

この画像カウンタを呼び出す HTML 側には以下のような感じで <img> タグを書きます。

<html>
<head>
  <title>テストページ</title>
</head>
<body>
  <p>
    総アクセス数: 
    <img src="Counter.aspx" width="75" height="20"/>
  </p>
</body>
</html>

おまけ(JavaScript 版)

同じ理屈で、JavaScript カウンタにしたりもできます。

<%@ Page language="c#" %>

<script runat="server">

private void Page_Load(object sender, System.EventArgs e)
{
  string text = (string)Session["count"];
  Response.ClearContent();
  Response.ContentType = "text/javascript";
  Response.Output.Write("document.write(" + text + ");");
}

</script>

これを JsCounter.aspx という名前で保存したとすると、 呼び出し側の HTML では以下のような書きます。

<html>
<head>
  <title>テストページ</title>
</head>
<body>
  <p>
    総アクセス数: 
    <script type="text/javascript" src="JsCounter.aspx"></script>
  </p>
</body>
</html>

更新履歴

ブログ