ASP.NET 案例分享:因應多站台透過 IHttpHandler 動態切換圖檔浮水印
【案例說明】
筆者任職公司的主要業務之一是同時維護 (代管) 多個網站,有的是所謂的企業 (品牌) 形象網站,也有購物網站的形式,其中的共通點是必須製作相當多的商品圖檔。這些圖檔從規劃、找 Model、拍攝、轉檔、修圖、加文字描述,以至於產出正式可用的完成品,期間所花費的人力、物力可謂曠日廢時,也因為對於品質要求有一定的水準,造成其他同業或網路賣家常常「借用」我們製作的圖檔,而我們商品部門的同事也得不定時上網巡查,並與這些同業或網路賣家好好溝通一番…,隨著類似的事件一再發生,將這些圖檔加上浮水印已成了必要手段。但問題來了…,這些圖檔約有一半的比例在我們維護的多個網站下共用,我們必須隨著站台的不同,切換圖檔浮水印的版權宣告內容 (以符合業主的觀感良好),加上圖檔存放所需的空間不小,我們需要的是在 Client 端透過網站要求檢視這些圖檔的時候,動態對輸出資料流附加浮水印,而不是對實體圖檔複製多份,然後再加上不同的浮水印,如果真的這樣做,日後的管理上不僅不方便,準備儲存空間的成本,其實也是一項負擔。
【構想】
綜合以上的需求,稍加整理條列如下:- 因應多站台切換圖檔浮水印內容。
- 在網頁要求時才動態附加圖檔浮水印。
需注意的是,這樣的調整會造成實體架構的變動,圖檔來源指向也要修改,所以網頁的程式碼大概都需要回頭修正,只是說需求就是如此的情況下,似乎也已經無可避免了…。
【實際操作】
有了大概的構想,接下來就是付諸行動,驗證是否可行了。對於 Http 處理常式的預備知識,可以先看看 ASP.NET - HOW TO:建立同步的 HTTP 處理常式,還有小喵大也有寫過兩篇非常棒的文章可供參考,建議還沒有接觸過這一塊的朋友可以先去看看:- ASP.NET Request 處理的過程 (瞭解 Http 處理常式所扮演的角色)
- 如何透過 HTTP Handler 讓 Web 專案中的圖案 (JPG) Response 時加上指定的文字 (加浮水印)
{
string tag = null;
if (context.Request.QueryString["tag"] != null) tag = context.Request.QueryString["tag"];
string copyright = GetCopyright(tag); // 版權宣告文字
Image logo = AssignLogo(tag); // 公司 Logo 圖檔
ImageWatermark output = new ImageWatermark(context.Request.PhysicalPath);
output.AddWatermark(copyright, logo);
output.Image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
這裡要做的是根據 QueryString 裡的 tag 識別值,判斷該附加甚麼樣的版權宣告文字以及 Logo,然後傳到自訂類別 ImageWatermark (寫在 WatermarkHandler 類別底下) 的 AddWatermark 方法去處理,最後再 Response 輸出串流,這是一個已經附加浮水印內容的圖檔。
接著來看 ImageWatermark 類別的程式碼:
{
public ImageWatermark(string physicalPathToImage)
{
bmp = new Bitmap(physicalPathToImage);
}
private Bitmap bmp = null;
public Bitmap Image
{
get
{
return bmp;
}
}
public void AddWatermark(string watermark, Image logo)
{
Graphics canvas = Graphics.FromImage(bmp);
#region 自動調整文字最適大小
int[] sizes = new int[] { 16, 14, 12, 10, 8, 6, 4 };
Font crFont = null;
SizeF crSize = new SizeF();
for (int i = 0; i < 7; i++)
{
crFont = new Font("arial", sizes[i], FontStyle.Bold);
crSize = canvas.MeasureString(watermark, crFont);
if ((ushort)crSize.Width < (ushort)bmp.Width)
break;
}
#endregion
#region 套印半透明、陰影效果的版權宣告浮水印到原始圖檔
// 指定文字位置
int yPixlesFromBottom = (int)(bmp.Height * .05);
float yPosFromBottom = ((bmp.Height - yPixlesFromBottom) - (crSize.Height / 2));
float xCenterOfImg = (bmp.Width / 2);
// 指定文字格式(置中對齊)
StringFormat StrFormat = new StringFormat();
StrFormat.Alignment = StringAlignment.Center;
// 指定文字筆刷(製造陰影效果)
SolidBrush semiTransBrush = new SolidBrush(Color.FromArgb(153, 255, 255, 255));
SolidBrush semiTransBrush2 = new SolidBrush(Color.FromArgb(153, 0, 0, 0));
canvas.DrawString(watermark, crFont, semiTransBrush2, new PointF(xCenterOfImg + 1, yPosFromBottom + 1), StrFormat);
canvas.DrawString(watermark, crFont, semiTransBrush, new PointF(xCenterOfImg, yPosFromBottom), StrFormat);
#endregion
#region 設定 Logo 圖檔的半透明效果
ImageAttributes imageAttributes = new ImageAttributes();
ColorMap colorMap = new ColorMap();
colorMap.OldColor = Color.FromArgb(255, 0, 255, 0);
colorMap.NewColor = Color.FromArgb(0, 0, 0, 0);
ColorMap[] remapTable = { colorMap };
imageAttributes.SetRemapTable(remapTable, ColorAdjustType.Bitmap);
float[][] colorMatrixElements = {
new float[] { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f },
new float[] { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f },
new float[] { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
new float[] { 0.0f, 0.0f, 0.0f, 0.3f, 0.0f },
new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }
};
ColorMatrix wmColorMatrix = new ColorMatrix(colorMatrixElements);
imageAttributes.SetColorMatrix(wmColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
#endregion
#region 套印 Logo 浮水印到原始圖檔
int xPosOfWm = ((bmp.Width - logo.Width) - 10);
int yPosOfWm = 10;
canvas.DrawImage(logo, new Rectangle(xPosOfWm, yPosOfWm, logo.Width, logo.Height), 0, 0, logo.Width, logo.Height, GraphicsUnit.Pixel, imageAttributes);
#endregion
logo.Dispose();
canvas.Dispose();
}
}
程式碼看起來「落落長」,但其實邏輯很簡單:ImageWatermark 類別的建構式會載入原始要求的圖檔,再作一連串的處理,依照指定的文字、Logo 附加到原圖檔上產出新圖,這部分會利用到 System.Drawing.Graphics 類別,這是 .NET 封裝的 GDI+ 圖形功能。
完成了 Http 處理常式,還必須在 Web.config 檔中加以註冊,還有在 IIS 中設定 HTTP 處理常式副檔名對應,才能讓功能正常運作,這部分請參考 MSDN Library:
【結語】
本篇文章的重點在於案例分享,一方面給有類似需求的朋友一個參考作法,另方面也是希望能有前輩高人指點一下,是否還有其他更好的解決方案可以提供給小弟嘗試看看。大家也可以看到,利用 Http 處理常式自訂 ASP.NET 輸出,以達到因應多站台動態切換圖檔浮水印的效果,說穿了其實沒有太高深的技術,但往往看似複雜的功能,仔細研究後,不過是自己嚇自己罷了…。線上 Demo:模擬多站台動態切換浮水印
完整 Sample:ImageHandler.rar
PS. Demo & Sample 所使用的是 Windows 7 作業系統內附的範例圖片,版權皆為 Microsoft 所有。