這個系列,請見
(1) 簡介
(2): ASP.NET 簡易實作圖形驗證
(3): ASP.NET 圖形加強版
上一篇加強了圖形後,發現了一個美中不足的部份。圖形是先產了圖檔後,再以 asp:image 元件指到該圖檔,讓使用者可以看圖猜字。但這樣長久下來,會讓圖檔愈來愈多,總有一天會讓硬碟爆掉。雖然可以定下MIS規則讓管理人員定期手動清理,但人是健忘的動物,還是有一天會忘的。
這一次,我們就來加強一下這個部份。
Generic Handler
我們將使用 Generic Handler來解決暫存圖檔的問題。首先新增一個 Captcha.ashx 到網站中。
由於在 ashx 中,我們要使用 Session,故必須實作 IRequiresSessionState,如下
public class Captcha : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/gif";
VerifyNow(context);
}
public bool IsReusable
{
get
{
return false;
}
}
接下來,經過重構之後,之前大部份關於Captcha 圖形驗證的程式都可以搬到 Captcha.ashx.cs 下了。程式如下
Captcha.ashx.cs
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Web;
using System.Web.SessionState;
namespace Captcha.Utility
{
/// <summary>
/// Summary description for Captcha
/// </summary>
public class Captcha : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/gif";
VerifyNow(context);
}
public bool IsReusable
{
get
{
return false;
}
}
private void VerifyNow(HttpContext context)
{
string randomString = context.Session["ImgText"] as string;
CreateImage(context, randomString);
}
private void CreateImage(HttpContext context, string imageText)
{
//建立一個 Bitmap。寬高未定,故先給定1, 1
Bitmap bmpImage = new Bitmap(1, 1);
//指定字型
Font font = new Font("Verdana", 24, FontStyle.Bold, GraphicsUnit.Point);
//進行繪圖。繪圖需透過 Grahpics 物件。
Graphics graphics = Graphics.FromImage(bmpImage);
//測量文字的寬高
int width = (int)graphics.MeasureString(imageText, font).Width;
int height = (int)graphics.MeasureString(imageText, font).Height;
//重新指定 Bitmap
bmpImage = new Bitmap(bmpImage, new Size(width, height));
//重新繪圖
Random r = new Random();
graphics = Graphics.FromImage(bmpImage);
graphics.Clear(r.NextColor()); //使用亂數底色
graphics.TextRenderingHint = TextRenderingHint.AntiAlias; //不要鋸齒
int avgWidth = width / imageText.Length;
for (int i = 0; i < imageText.Length; i++)
{
font = new Font("Verdana", r.Next(12, 24), FontStyle.Bold, GraphicsUnit.Point);
graphics.DrawString(imageText.Substring(i, 1), font, new SolidBrush(r.NextColor()), avgWidth * i, 0); //把文字畫上去
}
graphics.Flush();
for (int i = 0; i < 10; i++)
DrawRandomLine(graphics, height, width, r);
bmpImage.Save(context.Response.OutputStream, ImageFormat.Gif);
//記得 release 記憶體
graphics.Dispose();
font.Dispose();
bmpImage.Dispose();
}
private static Color NextColor(Random r)
{
return Color.FromArgb(r.Next(255), r.Next(255), r.Next(255));
}
private static void DrawRandomLine(Graphics graphics, int height, int width, Random random)
{
Pen pen = new Pen(random.NextColor());
pen.Width = random.Next(3);
Point p1 = new Point(random.Next(width), random.Next(height));
Point p2 = new Point(random.Next(width), random.Next(height));
graphics.DrawLine(pen, p1, p2);
}
}
}
網頁的部份,就只剩下取得密碼及驗證的部份
Sample1.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Sample1.aspx.cs" Inherits="Captcha.Sample1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Image runat="server" ID="image" ImageUrl="~/Utility/Captcha.ashx" />
<asp:TextBox runat="server" ID="txtAnswer" />
<asp:Button Text="輸入" runat="server" ID="btnSubmit" onclick="btnSubmit_Click" />
</div>
</form>
</body>
</html>
Sample1.aspx.cs
using System;
using System.Text;
using System.Web.UI;
namespace Captcha
{
public partial class Sample1 : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
CreateImageText();
}
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
string randomString = Session["ImgText"] as string;
if (randomString != txtAnswer.Text.Trim())
{
Response.Write("驗證錯誤");
CreateImageText();
}
else
Response.Write("驗證成功");
}
private void CreateImageText()
{
string randomString = GetRandomString(4);
Session["ImgText"] = randomString;
}
private string GetRandomString(int length)
{
char[] chars = @"23456789ABCDEFGHIJKLMNPQRSTUVWXYZ".ToCharArray();
Random r = new Random((int)DateTime.Now.Ticks);
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++)
sb.Append(chars[r.Next(chars.Length)]);
return sb.ToString();
}
}
}
結論
經過這樣一重構,ashx 的可重用性變高了,也不再需要圖片的暫存檔。
範例下載