在內容管理系統、電商平台、社群媒體等應用中,使用者上傳圖片後,系統通常需要自動生成縮略圖供列表顯示,並在原圖添加品牌浮水印以保護版權。本文將示範如何使用 System.Drawing 命名空間,在 ASP.NET WebForms 環境中實作這兩項核心功能。
System.Drawing.Image:抽象基類,提供 FromStream、FromFile、GetThumbnailImage 等靜態/實例方法System.Drawing.Graphics:繪圖介面,支援 DrawImage、DrawString 等繪製操作System.Drawing.Bitmap:像素格式圖片,繼承自 Image,可直接操作像素資料System.IO.Stream:串流操作,處理上傳檔案的 InputStreamusing System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Web.UI.HtmlControls;
public partial class ImageProcessor : System.Web.UI.Page
{
protected void btnUpload_Click(object sender, EventArgs e)
{
if (!fuImage.HasFile) return;
try
{
// 1. 驗證檔案類型
string extension = Path.GetExtension(fuImage.FileName).ToUpper();
string[] allowedExtensions = { ".JPG", ".JPEG", ".PNG", ".GIF", ".BMP" };
if (!Array.Exists(allowedExtensions, ext => ext == extension))
{
lblMessage.Text = "僅支援 JPG、PNG、GIF、BMP 格式";
return;
}
// 2. 準備路徑與檔名
string timestamp = DateTime.Now.ToString("yyyyMMddHHmmssfff");
string fileName = $"{timestamp}{extension}";
string originalPath = Server.MapPath("~/Images/Original/");
string thumbPath = Server.MapPath("~/Images/Thumb/");
string watermarkPath = Server.MapPath("~/Images/Watermark/logo.png");
// 確保目錄存在
foreach (string path in new[] { originalPath, thumbPath })
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
// 3. 載入圖片與浮水印
using (Stream uploadStream = fuImage.PostedFile.InputStream)
using (Image originalImage = Image.FromStream(uploadStream))
using (Image watermarkImage = Image.FromFile(watermarkPath))
{
// 4. 生成縮略圖 (維持長寬比,長邊 200px)
Image thumbImage = GenerateThumbnail(originalImage, 200);
thumbImage.Save(Path.Combine(thumbPath, fileName), GetImageFormat(extension));
// 5. 在原圖添加浮水印 (右下角)
using (Bitmap watermarkedBitmap = AddWatermark(originalImage, watermarkImage))
{
watermarkedBitmap.Save(Path.Combine(originalPath, fileName), GetImageFormat(extension));
}
lblMessage.Text = $"上傳成功!原圖:{fileName},縮略圖已生成。";
imgPreview.ImageUrl = $"~/Images/Thumb/{fileName}";
}
}
catch (Exception ex)
{
lblMessage.Text = $"處理失敗:{ex.Message}";
// 記錄完整堆疊追蹤至日誌系統
}
}
///
/// 生成縮略圖,維持長寬比
///
private Image GenerateThumbnail(Image source, int maxSideLength)
{
int width = source.Width;
int height = source.Height;
int newWidth, newHeight;
if (width > height)
{
newWidth = maxSideLength;
newHeight = (int)(height * (double)maxSideLength / width);
}
else
{
newHeight = maxSideLength;
newWidth = (int)(width * (double)maxSideLength / height);
}
// 使用高品質縮放
return source.GetThumbnailImage(newWidth, newHeight, () => false, IntPtr.Zero);
}
///
/// 在圖片右下角添加浮水印
///
private Bitmap AddWatermark(Image source, Image watermark)
{
Bitmap result = new Bitmap(source.Width, source.Height, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(result))
{
// 設定高品質繪圖
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
// 繪製原圖
g.DrawImage(source, 0, 0, source.Width, source.Height);
// 計算浮水印位置 (右下角,留 10px 邊距)
int x = source.Width - watermark.Width - 10;
int y = source.Height - watermark.Height - 10;
if (x < 0) x = 0;
if (y < 0) y = 0;
// 繪製浮水印 (支援透明度)
g.DrawImage(watermark, x, y, watermark.Width, watermark.Height);
}
return result;
}
///
/// 根據副檔名取得 ImageFormat
///
private ImageFormat GetImageFormat(string extension)
{
switch (extension.ToUpper())
{
case ".JPG": case ".JPEG": return ImageFormat.Jpeg;
case ".PNG": return ImageFormat.Png;
case ".GIF": return ImageFormat.Gif;
case ".BMP": return ImageFormat.Bmp;
default: return ImageFormat.Jpeg;
}
}
}
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ImageProcessor.aspx.cs" Inherits="Demo.ImageProcessor" %>
<!DOCTYPE html>
<html>
<head>
<title>圖片上傳處理示範</title>
<style>
.preview { max-width: 300px; border: 1px solid #ddd; padding: 5px; }
.msg { color: #d32f2f; font-weight: bold; margin: 10px 0; }
</style>
</head>
<body>
<form id="form1" runat="server" enctype="multipart/form-data">
<div>
<h3>圖片上傳與處理</h3>
<asp:FileUpload ID="fuImage" runat="server" Accept="image/*" />
<asp:Button ID="btnUpload" runat="server" Text="上傳並處理" OnClick="btnUpload_Click" />
<br /><br />
<asp:Label ID="lblMessage" runat="server" CssClass="msg"></asp:Label>
<br />
<asp:Image ID="imgPreview" runat="server" CssClass="preview" Visible="false" />
</div>
</form>
</body>
</html>
使用 ColorMatrix 與 ImageAttributes 實現半透明浮水印:
float[][] matrix = {
new float[] {1, 0, 0, 0, 0},
new float[] {0, 1, 0, 0, 0},
new float[] {0, 0, 1, 0, 0},
new float[] {0, 0, 0, 0.5f, 0}, // Alpha 0.5 = 50% 透明度
new float[] {0, 0, 0, 0, 1}
};
ColorMatrix cm = new ColorMatrix(matrix);
ImageAttributes ia = new ImageAttributes();
ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
g.DrawImage(watermark, new Rectangle(x, y, watermark.Width, watermark.Height),
0, 0, watermark.Width, watermark.Height, GraphicsUnit.Pixel, ia);
private void DrawTextWatermark(Graphics g, string text, Font font, Brush brush, int x, int y)
{
// 添加陰影效果增強可讀性
g.DrawString(text, font, Brushes.Black, x + 1, y + 1);
g.DrawString(text, font, brush, x, y);
}
搭配 Task.Run 或背景工作佇列,避免阻塞 Web 執行緒:
await Task.Run(() => ProcessImageAsync(fileStream, fileName));
using 區塊確保 IDisposable 物件正確釋放Image.GetThumbnailImageAbort 回調| 問題 | 原因 | 解決方案 |
|---|---|---|
| GDI+ 一般性錯誤 | 串流位置未重置、權限不足、檔案被鎖定 | stream.Position = 0、確保目錄寫入權限、使用 FileShare.Read |
| 縮略圖失真/模糊 | 預設雙三次插值品質不足 | 使用 Graphics.DrawImage 搭配 HighQualityBicubic |
| PNG 透明背景變黑 | 儲存時未保留 Alpha 通道 | 使用 PixelFormat.Format32bppArgb 建立 Bitmap |
| 記憶體溢位 | 大量大圖同時處理 | 限制並發數、使用串流分塊、升級至 64 位元進程 |
ASP.NET 結合 System.Drawing 提供了強大的伺服器端圖片處理能力。透過合理的架構設計、錯誤處理與效能優化,可以構建穩健的圖片上傳處理管線。現代雲端應用建議考慮使用 ImageSharp、SkiaSharp 等跨平台庫,或採用雲端圖片處理服務,以支援容器化部署與 Linux 環境。