| 策略 | 時機 | 優點 | 缺點 |
|---|---|---|---|
| 上傳時合成 | SaveAs 後立即 GDI+ 繪製 | 請求時直接輸出靜態檔,CPU 壓力低 | 原圖永久改變、無法移除/更換浮水印 |
| 請求時合成 (本文方案) | HttpHandler ProcessRequest | 原圖完整保留、浮水印可隨時調整/關閉 | 每次請求消耗 CPU/記憶體、需快取緩解 |
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
namespace BookShop.Web
{
public class WaterMark : IHttpHandler
{
private const string WatermarkPath = "~/Images/watermark.png"; // 建議 PNG 透明背景
private const string DefaultImagePath = "~/Images/default.jpg";
public bool IsReusable => false;
public void ProcessRequest(HttpContext context)
{
string physicalPath = context.Request.PhysicalPath;
Image sourceImage;
// 1. 取得來源圖片
if (File.Exists(physicalPath))
{
sourceImage = Image.FromFile(physicalPath);
}
else
{
// 找不到檔案 → 輸出預設圖
sourceImage = Image.FromFile(context.Server.MapPath(DefaultImagePath));
}
try
{
// 2. 載入浮水印
using (Image watermark = Image.FromFile(context.Server.MapPath(WatermarkPath)))
using (Graphics g = Graphics.FromImage(sourceImage))
{
// 高品質繪圖設定
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// 3. 計算位置:右下角對齊,留 10px 邊距
int x = sourceImage.Width - watermark.Width - 10;
int y = sourceImage.Height - watermark.Height - 10;
if (x < 0) x = 0;
if (y < 0) y = 0;
// 4. 繪製浮水印
g.DrawImage(watermark, x, y, watermark.Width, watermark.Height);
}
// 5. 輸出 JPEG (可依原圖格式動態決定)
context.Response.Clear();
context.Response.ContentType = "image/jpeg";
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetMaxAge(TimeSpan.FromDays(30)); // 瀏覽器快取 30 天
context.Response.Cache.SetLastModified(File.GetLastWriteTimeUtc(physicalPath));
sourceImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
finally
{
sourceImage?.Dispose(); // 確保釋放非託管資源
}
}
}
}
<system.webServer>
<handlers>
<add name="WaterMarkHandler"
path="*.jpg" verb="GET"
type="BookShop.Web.WaterMark, BookShop.Web"
preCondition="integratedMode" />
<!-- 若需支援 png/gif,複製並修改 path -->
</handlers>
</system.webServer>
HttpHandler 動態浮水印保護原圖完整性,適合「浮水印策略頻繁變更」或「需保留原圖供下載」的場景。生產部署務必加上**快取層 (MemoryCache + HTTP 304 + CDN)**,否則高併發下 GDI+ 會成為效能瓶頸。