這個系列,請見
(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 的可重用性變高了,也不再需要圖片的暫存檔。
範例下載
沒有留言:
張貼留言