傳統縮略圖方案需預先產生實體檔或仰賴獨立 HTTP Handler。本控制項採用「單一類別、編譯為 DLL、無外部依賴」架構:利用 ASP.NET 網頁生命週期,將縮略圖請求導向包含控制項的頁面本身,透過查詢字串區分一般頁面請求與圖片請求,即時以 GDI+ 縮放原圖並直接輸出至回應流,完全不落地儲存。
Default.aspx)。Render 階段輸出 <img src="Default.aspx?thumbnail=original.jpg&w=100&h=75..." />。img,發起第二次請求同一頁面但帶縮略圖參數。Request["thumbnail"],啟動圖片處理邏輯:讀取原圖 → GDI+ 縮放 → 輸出至 Response.OutputStream → Response.End()。[DefaultProperty("ImageUrl")]
[ToolboxData("<{0}:Thumbnail runat=server>{0}:Thumbnail>")]
[Designer(typeof(Designer.ThumbnailDesigner))]
public class Thumbnail : WebControl {
public Thumbnail() : base(HtmlTextWriterTag.Img) {
Width = Unit.Parse("100");
Height = Unit.Parse("75");
}
// 屬性:ImageUrl, JpegQuality, Width, Height, ImageAlign, AlternateText 等
// 皆支援 ViewState 持久化、設計時編輯器、預設值
protected override void AddAttributesToRender(HtmlTextWriter writer) {
if (string.IsNullOrEmpty(Page.Request["thumbnail"])) {
// 一般頁面請求:輸出指向自身的 img src(含查詢參數)
writer.AddAttribute(HtmlTextWriterAttribute.Src,
Page.Request.Url.AbsolutePath + "?" + GetQueryString());
// 其他屬性:alt, align, border...
}
}
private string GetQueryString() {
var sb = new StringBuilder();
sb.Append("thumbnail=" + Page.Server.HtmlEncode(ImageUrl));
sb.Append("&w=" + Width.Value);
sb.Append("&h=" + Height.Value);
sb.Append("&bc=" + BackColor.ToArgb());
sb.Append("&jq=" + JpegQuality);
return sb.ToString();
}
protected override void OnInit(EventArgs e) {
base.OnInit(e);
Init += Thumbnail_Init;
PreRender += Thumbnail_PreRender;
}
void Thumbnail_Init(object sender, EventArgs e) => RenderThumbnailImage(Request, Response);
void Thumbnail_PreRender(object sender, EventArgs e) => RenderThumbnailImage(Request, Response);
static void RenderThumbnailImage(HttpRequest request, HttpResponse response) {
if (string.IsNullOrEmpty(request["thumbnail"])) return;
var thumb = new Thumbnail {
ImageUrl = request["thumbnail"],
Width = Unit.Parse(request["w"] ?? "100"),
Height = Unit.Parse(request["h"] ?? "75"),
BackColor = Color.FromArgb(int.Parse(request["bc"] ?? "0")),
JpegQuality = int.Parse(request["jq"] ?? "80")
};
Bitmap bmp;
try { bmp = thumb.GetThumbnail(); }
catch { bmp = Properties.Resources.GenerateImageError; }
response.Clear();
string ext = thumb.ImageUrl.Split('.').Last().ToLower();
if (ext == "gif") {
response.ContentType = "image/gif";
bmp.Save(response.OutputStream, ImageFormat.Gif);
} else if (ext == "jpg" || ext == "jpeg") {
response.ContentType = "image/jpeg";
// 套用 JPEG 品質編碼器參數
var encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, (long)thumb.JpegQuality);
var jpegCodec = ImageCodecInfo.GetImageEncoders().First(c => c.FormatDescription == "JPEG");
bmp.Save(response.OutputStream, jpegCodec, encoderParams);
} else {
response.ContentType = "image/bmp";
bmp.Save(response.OutputStream, ImageFormat.Bmp);
}
bmp.Dispose();
response.Flush();
response.End(); // 關鍵:中止後續頁面生命週期
}
Bitmap GetThumbnail() {
Image original;
try {
if (DesignMode) original = Properties.Resources.ThumbnailDesign;
else original = Image.FromFile(Context.Server.MapPath(ImageUrl));
} catch { return Properties.Resources.GenerateImageError; }
Size newSize = GetNewSize((int)Width.Value, (int)Height.Value, original.Width, original.Height);
var outBmp = new Bitmap((int)Width.Value, (int)Height.Value);
using (var g = Graphics.FromImage(outBmp)) {
g.CompositingQuality = CompositingQuality.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.Clear(BackColor);
var rect = new Rectangle(
(int)Math.Round((Width.Value - newSize.Width) / 2.0),
(int)Math.Round((Height.Value - newSize.Height) / 2.0),
newSize.Width, newSize.Height);
g.DrawImage(original, rect, 0, 0, original.Width, original.Height, GraphicsUnit.Pixel);
}
original.Dispose();
return outBmp;
}
Size GetNewSize(int maxW, int maxH, int w, int h) {
double ratio = Math.Min((double)maxW / w, (double)maxH / h);
return new Size((int)(w * ratio), (int)(h * ratio));
}
}
Thumbnail 至 .aspx 頁面,設定 ImageUrl、Width、Height、JpegQuality 等屬性。GetThumbnail 中繪製文字/圖形)。ImageUrl + Width + Height 為 Key 存放 MemoryCache,避免重複運算)。Mode 屬性:Crop(裁切)、Stretch(拉伸)、Fit(等比縮放,現行預設)。