System.Drawing.Image 物件。Bitmap,使用 Graphics 高品質插值模式繪製縮圖。protected void btnUpload_Click(object sender, EventArgs e)
{
// 1. 驗證檔案類型
HttpPostedFile postedFile = uploadFile.PostedFile;
if (postedFile == null || postedFile.ContentLength == 0)
{
lblMsg.Text = "請選擇檔案";
return;
}
if (!postedFile.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
{
lblMsg.Text = "僅允許上傳圖片檔案";
return;
}
// 2. 讀取原圖
Image originalImage;
try
{
using (Stream inputStream = postedFile.InputStream)
{
originalImage = Image.FromStream(inputStream);
}
}
catch (Exception ex)
{
lblMsg.Text = "圖片讀取失敗: " + ex.Message;
return;
}
int origW = originalImage.Width;
int origH = originalImage.Height;
const int maxThumbSize = 100;
// 3. 計算縮略圖尺寸 (保持比例)
int thumbW, thumbH;
if (origW >= origH)
{
thumbW = maxThumbSize;
thumbH = (int)Math.Floor((double)origH * maxThumbSize / origW);
}
else
{
thumbH = maxThumbSize;
thumbW = (int)Math.Floor((double)origW * maxThumbSize / origH);
}
// 4. 生成高品質縮略圖
Bitmap thumbBitmap = null;
Graphics g = null;
try
{
thumbBitmap = new Bitmap(thumbW, thumbH);
g = Graphics.FromImage(thumbBitmap);
// 關鍵:高品質插值與平滑模式
g.InterpolationMode = InterpolationMode.HighQualityBicubic; // 雙三次插值,品質最佳
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.Clear(Color.Transparent); // 透明背景
g.DrawImage(originalImage,
new Rectangle(0, 0, thumbW, thumbH),
new Rectangle(0, 0, origW, origH),
GraphicsUnit.Pixel);
}
finally
{
if (g != null) g.Dispose();
}
// 5. 產生唯一檔名 (yyyyMMddHHmmssfff)
string timestamp = DateTime.Now.ToString("yyyyMMddHHmmssfff");
string uploadDir = Server.MapPath("~/Uploads/");
if (!Directory.Exists(uploadDir)) Directory.CreateDirectory(uploadDir);
string originalPath = Path.Combine(uploadDir, "orig_" + timestamp + ".jpg");
string thumbPath = Path.Combine(uploadDir, "thumb_" + timestamp + ".jpg");
// 6. 儲存 (統一轉 JPEG,品質 90%)
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
try
{
originalImage.Save(originalPath, jpegCodec, ep);
thumbBitmap.Save(thumbPath, jpegCodec, ep);
lblMsg.Text = "上傳成功!原圖: " + Path.GetFileName(originalPath) + " 縮圖: " + Path.GetFileName(thumbPath);
// 顯示縮圖預覽
imgPreview.ImageUrl = "~/Uploads/" + Path.GetFileName(thumbPath);
}
catch (Exception ex)
{
lblMsg.Text = "儲存失敗: " + ex.Message;
}
finally
{
// 7. 嚴格釋放資源 (關鍵!)
originalImage.Dispose();
if (thumbBitmap != null) thumbBitmap.Dispose();
}
}
private ImageCodecInfo GetEncoderInfo(string mimeType)
{
foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
{
if (codec.MimeType == mimeType) return codec;
}
return null;
}
| 技術點 | 說明 | 最佳實務 |
|---|---|---|
| InterpolationMode | 縮放演算法 | HighQualityBicubic (雙三次) 品質最佳;HighQualityBilinear 次之;NearestNeighbor 最快但鋸齒嚴重 |
| SmoothingMode | 邊緣平滑 | HighQuality 配合插值模式 |
| CompositingQuality | 合成品質 | HighQuality 減少色帶 |
| PixelOffsetMode | 像素偏移 | HighQuality 提升銳度 |
| Encoder.Quality | JPEG 壓縮品質 (0-100) | 85-95 平衡檔案大小與視覺品質 |
| 資源釋放 | 非受控 GDI+ 物件 | 必須在 finally 區塊 Dispose(),否則導致 GDI+ 物件洩漏、程序崩潰 |
postedFile.ContentType 決定 Encoder (PNG 保留透明、JPEG 相片)。<httpRuntime maxRequestLength="10240" /> 10MB)。web.config 設定 <handlers> 封鎖)。ASP.NET 圖片上傳與縮略圖生成的核心在於:**正確驗證、高品質 GDI+ 繪製 (InterpolationMode.HighQualityBicubic)、嚴格資源釋放 (Dispose)、安全儲存策略**。掌握這些細節,能避免常見的模糊縮圖、GDI+ 記憶體洩漏、上傳目錄被植入惡意腳本等生產環境災難。