Web 應用常需生成圖片縮略圖供列表預覽。.NET Framework 內建 System.Drawing 命名空間提供完整 GDI+ 封裝,無需第三方元件即可實現高品質縮略圖生成,支援 JPEG、PNG、TIFF、BMP 等主流格式。
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using System.Web;
using System.Web.UI;
namespace Exam_C {
public class ToThumbnailImage : Page {
// 支援的副檔名與 MIME 對應
static Hashtable htmimes = new Hashtable();
readonly string AllowExt = ".jpe|.jpeg|.jpg|.png|.tif|.tiff|.bmp";
protected override void OnInit(EventArgs e) {
// 初始化 MIME 對應表
htmimes[".jpe"] = "image/jpeg";
htmimes[".jpeg"] = "image/jpeg";
htmimes[".jpg"] = "image/jpeg";
htmimes[".png"] = "image/png";
htmimes[".tif"] = "image/tiff";
htmimes[".tiff"] = "image/tiff";
htmimes[".bmp"] = "image/bmp";
// 呼叫生成範例
ToThumbnailImages("lionsky.jpg", "b.gif", 300);
}
// 取得指定 MIME 的編碼器
static ImageCodecInfo GetCodecInfo(string mimeType) {
foreach (ImageCodecInfo ici in ImageCodecInfo.GetImageEncoders())
if (ici.MimeType == mimeType) return ici;
return null;
}
// 驗證副檔名
bool CheckValidExt(string ext) {
foreach (string allowed in AllowExt.Split('|'))
if (allowed.ToLower() == ext.ToLower()) return true;
return false;
}
// 儲存影像(支援品質參數)
void SaveImage(Image image, string path, ImageCodecInfo ici) {
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
image.Save(path, ici, ep);
ep.Dispose();
}
// 主要縮略圖生成邏輯
public void ToThumbnailImages(string sourcePath, string thumbPath, int thumbWidth) {
string src = sourcePath;
string dest = thumbPath;
int targetWidth = thumbWidth;
string ext = src.Substring(src.LastIndexOf('.')).ToLower();
if (string.IsNullOrEmpty(src))
throw new NullReferenceException("SourceImagePath is null!");
if (!CheckValidExt(ext))
throw new ArgumentException($"不支援格式,允許: {AllowExt}", "SourceImagePath");
// 載入原圖
Image img = Image.FromFile(HttpContext.Current.Server.MapPath(src));
int srcW = img.Width, srcH = img.Height;
// 計算目標尺寸(保持比例)
int targetHeight = (targetWidth * 3) / 4; // 預設 4:3
if (((double)srcW / srcH) >= 1.3333333333333333) // 寬圖
targetHeight = (srcH * targetWidth) / srcW;
else // 高圖
targetWidth = (srcW * targetHeight) / srcH;
if (targetWidth < 1 || targetHeight < 1) return;
// 建立高品質畫布
Bitmap bmp = new Bitmap(targetWidth, targetHeight, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(bmp)) {
g.Clear(Color.Transparent);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(img, new Rectangle(0, 0, targetWidth, targetHeight));
}
img.Dispose();
try {
string savePath = string.IsNullOrEmpty(dest) ? src : dest;
SaveImage(bmp, HttpContext.Current.Server.MapPath(savePath), GetCodecInfo((string)htmimes[ext]));
} finally {
bmp.Dispose();
}
}
}
}
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L); // 90% 品質
image.Save(path, ici, ep);
JPEG 壓縮品質 90% 通常在檔案大小與視覺品質間取得最佳平衡。
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
雙三次插值比預設雙線性插值產生更銳利、較少鋸齒的縮略圖。
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb); // 32-bit 帶 Alpha
g.Clear(Color.Transparent);
保留 PNG 透明通道,避免白底干擾。
using (Graphics g = Graphics.FromImage(bmp)) { ... } // 確保 Graphics 釋放
img.Dispose(); // 釋放原圖非受控資源
bmp.Dispose(); // 釋放目標位圖
GDI+ 物件包裝非受控記憶體,必須明確 Dispose,避免記憶體洩漏與 GDI+ 物件耗盡。
<%@ Page Language="c#" Codebehind="ToThumbnailImage.aspx.cs"
Src="ToThumbnailImage.aspx.cs" AutoEventWireup="false"
Inherits="Exam_C.ToThumbnailImage" %>
生成縮略圖
| 議題 | 解決方案 |
|---|---|
| 效能瓶頸 | 引入快取機制(記憶體/磁碟/Redis),避免重複生成 |
| 大圖記憶體壓力 | 使用 Image.GetThumbnailImageAbort 回調或分塊處理 |
| 併發存取 | 檔案鎖定或快取雙重檢查鎖定 |
| 格式擴充 | 加入 WebP、AVIF 支援(需外部庫) |
| 安全性 | 驗證檔案魔數而非僅副檔名,防惡意上傳 |
| 雲端部署 | Azure Functions / AWS Lambda 無伺服器縮圖服務 |
System.Drawing 提供完整伺服器端影像處理能力,配合編碼器參數控制、插值模式選擇、資源正確釋放,可構建穩健的縮略圖生成服務。現代雲原生架構建議將影像處理剝離為獨立微服務,採用 ImageSharp、SkiaSharp 等跨平台庫替代 Windows 專屬的 GDI+ 封裝。