首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

ASP.NET 圖片處理實戰:縮略圖生成與浮水印添加完整指南

     編輯:關於ASP.NET

需求背景

在內容管理系統、電商平台、社群媒體等應用中,使用者上傳圖片後,系統通常需要自動生成縮略圖供列表顯示,並在原圖添加品牌浮水印以保護版權。本文將示範如何使用 System.Drawing 命名空間,在 ASP.NET WebForms 環境中實作這兩項核心功能。

核心類別與方法介紹

  • System.Drawing.Image:抽象基類,提供 FromStream、FromFile、GetThumbnailImage 等靜態/實例方法
  • System.Drawing.Graphics:繪圖介面,支援 DrawImage、DrawString 等繪製操作
  • System.Drawing.Bitmap:像素格式圖片,繼承自 Image,可直接操作像素資料
  • System.IO.Stream:串流操作,處理上傳檔案的 InputStream

完整程式碼實作

using 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>

進階優化與擴展

1. 浮水印透明度控制

使用 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);

2. 文字浮水印支援

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);
}

3. 非同步處理大量圖片

搭配 Task.Run 或背景工作佇列,避免阻塞 Web 執行緒:

await Task.Run(() => ProcessImageAsync(fileStream, fileName));

4. 記憶體管理最佳實踐

  • 嚴格使用 using 區塊確保 IDisposable 物件正確釋放
  • 大圖片處理考慮分塊處理或使用 Image.GetThumbnailImageAbort 回調
  • 監控 Gen 2 GC 收集頻率,避免大型物件堆積

常見問題與解決方案

問題原因解決方案
GDI+ 一般性錯誤 串流位置未重置、權限不足、檔案被鎖定 stream.Position = 0、確保目錄寫入權限、使用 FileShare.Read
縮略圖失真/模糊 預設雙三次插值品質不足 使用 Graphics.DrawImage 搭配 HighQualityBicubic
PNG 透明背景變黑 儲存時未保留 Alpha 通道 使用 PixelFormat.Format32bppArgb 建立 Bitmap
記憶體溢位 大量大圖同時處理 限制並發數、使用串流分塊、升級至 64 位元進程

總結

ASP.NET 結合 System.Drawing 提供了強大的伺服器端圖片處理能力。透過合理的架構設計、錯誤處理與效能優化,可以構建穩健的圖片上傳處理管線。現代雲端應用建議考慮使用 ImageSharp、SkiaSharp 等跨平台庫,或採用雲端圖片處理服務,以支援容器化部署與 Linux 環境。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved