Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

ashxファイルを利用したアクセスカウンタの実装

ASP.NETでの画像の自動生成とダウンロード

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2005/07/15 12:00

ダウンロード サンプルソース (4.1 KB)

.NET Frameworkには画像を生成するためのクラスが含まれています。本稿では、アクセスカウンタの実装サンプルを通して、ASP.NETで画像を自動的に生成しブラウザにダウンロードする方法を解説します。

アクセスカウンタの実行例
アクセスカウンタの実行例

はじめに

 .NET Frameworkには画像を生成するためのクラスが含まれています。このクラスを使えばASP.NETの中で画像を自動生成させることが簡単に実現できます。また、アクセスカウンタの実行例では、画像の生成とダウンロードにashxファイルを利用しています。ashxファイルの利用はドキュメントに説明されていませんが、画像のようなデータを直接ブラウザにダウンロードする場合に適した方法です。

 ここではアクセスカウンタの実装を例に、画像を自動生成する方法、およびashxファイルの利用方法について説明します。

対象読者

 .NET Frameworkを用いてWebアプリケーションを開発している方。

必要な環境

 SQL ServerASP.NETが実行できるWindows XP、またはWindows 2003 Server。

サンプルの実行環境の構築

 サンプルではカウンタのデータの保持にSQL Serverを利用しています。データベース側の準備として、まずテーブルの生成と初期データ(カウンタID:1、初期値:0)の追加を行うために以下のSQL文をクエリアナライザなどから実行してください。

テーブルの生成と初期データのインサート
CREATE TABLE [dbo].[counterTable] (
    [counterID] [int] NOT NULL ,
    [number] [int] NOT NULL 
) ON [PRIMARY]
GO

INSERT INTO counterTable(counterID, number) VALUES(1, 0)
GO

 複数のカウンタを利用したい場合には、カウンタIDが1以外のデータを必要なだけ追加してください。

 次にストアドプロシージャを登録します。以下のSQL文を同じくクエリアナライザなどから実行してください。

ストアドプロシージャの生成
CREATE PROCEDURE GETCOUNT
@param1 int
AS
UPDATE COUNTERTABLE SET NUMBER = NUMBER + 1 WHERE COUNTERID = @param1
SELECT NUMBER FROM COUNTERTABLE  WHERE COUNTERID = @param1
GO

 このストアドプロシージャは呼び出すときに引数としてカウンタIDを必要とします。処理としては、渡されたカウンタIDのデータを1つ繰り上げてその数字を返します。ここまででデータベース側の準備は終了です。

補足説明:データベースの利用について
 レンタルサーバでも最近はcerviのように安価でSQL Serverを利用できるところが出てきています。こういったサーバ上ではテキストファイルを利用する(ASP.NETの実行ユーザにファイルの書き込み権限を付与する必要がある)よりも、データベースを利用するほうが手軽です。
 自分でデータベースを含めてサーバを管理している場合は、ASP.NETの実行ユーザにデータベースのアクセス権を付与する必要があります。このようなASP.NETからデータベースを利用する方法については書籍『ASP.NETでいってみよう』で学習することをおすすめします。

 次にサンプルソースに含まれる「counter.ashx」、「countertest.html」の2つのファイルをサーバ上の同じフォルダに配置します。このとき「counter.ashx」の中に記述されているデータベースへの接続文字列を、利用しているサーバに合わせて修正する必要があります。この時点で「countertest.html」にアクセスし、カウンタが正常に表示されればOKです。

 なお、各種設定の変更については、プログラム中のコメントと「ReadMe.txt」を参照してください。ここで「countertest.html」ファイルでカウンタを表示するために記述しているタグは以下のようになっています。

<img src="counter.ashx?ID=1">

 これは普通のimgタグですので、「countertest.html」のようにhtmlファイルからでも利用可能です。カウンタを複数設置する場合は、imgタグ内でIDに渡す数字をデータベースに作成した別のカウンタIDに合わせてください。

ashxファイルの概要

 ashxファイルはIHttpHandlerインタフェースを実装したクラスを、動的にコンパイルして実行してくれるファイルです。

 IHttpHandlerインタフェースはASP.NETの処理の中核といえるインタフェースであり、PageクラスもIHttpHandlerインタフェースを継承しています。Pageクラスのようにユーザインタフェースを提供するためのさまざまな機能は持っていませんが、それだけにサンプルのような画像などのデータを直接ダウンロードする場合にはashxファイルの利用が適しています。

 ashxファイルは以下のような構造をとります。

ashxファイルの構造
<%@ WebHandler language="C#" class="MyImage" %>

public class MyImage:IHttpHandler
{

    public bool IsReusable
    {
        get
        {
            return(true);
        }
    }

    public void ProcessRequest(HttpContext context)
    {
        ・・・
    }

}

 1行目はWebHandlerディレクティブです。ここでは使用する言語と作成するクラス名を属性として記述します。作成するクラスはIHttpHandlerインタフェースを継承するのでIsReusableメソッドとProcessRequestメソッドを実装しなければなりません。

 IsReusableメソッドはこのクラスが再利用できるかどうかをASP.NETが判定するためのメソッドです。通常trueを返すように実装しておけば問題ないでしょう。

 ProcessRequestメソッドの実装がプログラミング作業の中心になります。このメソッドに渡されるHttpContextオブジェクトを通してブラウザからのリクエストを取り出し、ブラウザに渡すレスポンスを作成していくことになります。サンプルでは説明がしやすいようにいくつかの内部メソッドを呼び出す形でここの処理を行っています。順を追って説明していきます。

ブラウザから渡されるデータの取り出し

 最初にブラウザから渡されるカウンタIDの値を取り出しています。

ブラウザから渡されるIDの取り出し
private string getCounterID(HttpContext context)
{
    return context.Request.QueryString["ID"];
}

 ブラウザからはクエリ文字列によってカウンタIDの値が渡されてきます。この場合、HttpContextオブジェクトが持つRequestオブジェクトを通してその値を取り出します。

再読み込み時にカウントアップしないための工夫

 再読み込みによりカウンタの数字が増えてしまうのを防ぐため、サンプルではCookieを利用しています。

再読み込みのチェック
private string checkCookie(HttpContext context)
{
    // 同じブラウザから同じセッション内で接続された際は
    // Cookieに保存してある数字を使う
    if(context.Request.Cookies["counter"]!=null)
        return context.Request.Cookies["counter"].Value;
    else
        return "";
}

 Requestオブジェクトを通してCookieの存在をチェックし、その値を取り出しています。Cookieにはカウンタに表示された数字を保存していますので、再読み込み時にはその数字を利用し、データベースへの接続を行いません。

ストアドプロシージャを利用したデータの取り出し

 Cookieが存在していない場合、つまりブラウザからの最初の接続のときはデータベースからカウンタに表示する数字を取り出します。

データベースからカウンタIDに対応するカウント数の取り出し
private string getCount(string id)
{
    string countString;

    if(id==null)
        countString =  errString;
    else
    {
        SqlConnection cn = new SqlConnection(connectionString);
        // コマンドをストアドプロシージャを利用するように設定
        SqlCommand cmd = new SqlCommand(spName, cn);
        cmd.CommandType = CommandType.StoredProcedure;
        // パラメータの設定
        SqlParameter p1 =
            cmd.Parameters.Add("@param1", SqlDbType.Int);
        p1.Value = int.Parse(id);

        try
        {
            cn.Open();
            // データベースから数字を取り出し
            // 指定された桁数まで0で埋める
            countString = cmd.ExecuteScalar().ToString()
                .PadLeft(countLength, '0');
        }
        catch
        {
            countString = errString;
        }
        finally
        {
            cn.Close();
        }
    }
    return countString;
}

 ストアドプロシージャを利用する場合はSqlCommandオブジェクトのCommndTypeプロパティにCommandType.StoredProcedureを設定します。また、ここで利用するストアドプロシージャは単一の値を返しますので、ExecuteScalarメソッドを利用してその値を取り出しています。

カウンタ画像の生成

 カウンタ画像の生成は多少複雑な処理になっています。

カウンタ画像の生成
private Bitmap getBmp(string cnt)
{
    // StringFormatの設定
    StringFormat strfmt = new StringFormat();
    CharacterRange[] characterRanges = 
        {new CharacterRange(0, cnt.Length)};
    strfmt.SetMeasurableCharacterRanges(characterRanges);
    strfmt.LineAlignment = StringAlignment.Center;

    // ベースになる画像の作成
    Bitmap bmp = new Bitmap(baseWidth, baseHeight);
    Graphics g = Graphics.FromImage(bmp);
    g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
    g.FillRectangle(new SolidBrush(Color.FromName(bgColor)), 
        0, 0, baseWidth, baseHeight);

    // 数字の描き込み
    Font f = new Font(fontName, fontSize);
    g.DrawString(cnt, f, new SolidBrush(Color.FromName(fontColor)),
        new RectangleF(0, 0, baseWidth, baseHeight), strfmt)

    // 数字を書いた部分の大きさの計測
    Region[] rg = g.MeasureCharacterRanges( cnt, f ,
        new RectangleF(0, 0, baseWidth, baseHeight), strfmt); 
    RectangleF rfbase = rg[0].GetBounds(g); 
    int iEmHeight = f.FontFamily.GetEmHeight(f.Style);
    int iCellAscent = f.FontFamily.GetLineSpacing(f.Style) - 
        f.FontFamily.GetCellDescent(f.Style);
    RectangleF rf = new RectangleF(rfbase.Left-1, rfbase.Top, 
        rfbase.Width+2, rfbase.Height / iEmHeight * iCellAscent);

    // 数字部分の切り出しと枠線の描き込み
    Bitmap cb = bmp.Clone(rf, bmp.PixelFormat);
    Graphics cg = Graphics.FromImage(cb);
    cg.DrawRectangle(new Pen(Color.FromName(borderColor)), 
        0, 0, rf.Width-1, rf.Height-1);

    return cb;
}

 カウンタに表示する数字のフォント、桁数を設定で変更できるようにしています。このフォントや桁数に合わせてカウンタ画像の大きさを変更するため、一度文字を書き込んでその文字の大きさを計測し、必要な部分だけを切り出してダウンロード用の画像とする、という処理を記述しています。

画像のダウンロード

 画像のダウンロード時には、ダウンロードするデータのMIME Typeを指定する必要があります。

画像の形式指定とダウンロード(ProcessRequestメソッド)
// 画像の形式を指定してメモリ上に保存
MemoryStream mem = new MemoryStream();
counterBmp.Save(mem, ImageFormat.Png);

// 画像のダウンロード
context.Response.ContentType = "image/png";
context.Response.BinaryWrite(mem.ToArray());

 サンプルでは生成したカウンタ画像をPNG形式に保存し、ContrentTypeにPNGのMIME Typeを指定した上で、BinaryWriteメソッドを利用して画像をダウンロードしています。

まとめ

 ASP.NETは柔軟性が非常に高く、さまざまな処理を独自の実装に置き換えることが可能です。ashxファイルはaspxファイルを置き換えることができるものであり、そういったASP.NETの柔軟性を学ぶ最初の手がかりとして手軽に取り組むことができる便利なファイルです。

 残念ながらVisualStudio.NET 2003ではashxファイルはサポートされておらず、インテリセンスなどは利用できませんが、ぜひこの記事を参考に活用していただければと思います。

参考資料

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 小野 修司(オノ シュウジ)

    MVP for Visual Developer - Visual C# あおい情報システム株式会社勤務。 .NETに関する話題を扱う「どっとねっとふぁん」を運営。 &nbsp;

All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5