C#利用 System.Drawing 实现图片水印与缩略图处理

向北走
向北走
发布时间:2026-10-13 | 4人浏览过
原创

在 Web 开发或桌面应用中,经常需要对用户上传的图片进行统一处理,包括自动裁剪缩略图和添加品牌标识。基于 .NET Framework 中的 System.Drawing 库,我们可以编写一个通用的图像处理工具类,实现等比例缩略图生成、图片叠层水印及半透明文字水印功能。

实现思路与核心逻辑

所有操作均围绕 System.Drawing 命名空间展开。核心步骤包括:加载源图像 -> 创建 Graphics 实例 -> 执行绘图指令(绘制背景图、水印图或文字) -> 保存结果。为确保性能,必须严格管理非托管资源的释放,特别是在捕获异常时仍需执行清理工作。

准备条件

项目需引用以下 DLL:
System.Drawing.dll
System.Web.dll (若在 ASP.NET 环境下运行以获取服务器路径)

操作步骤

第1步:计算等比例缩小后的尺寸

目的:防止图片变形,确保缩略图在指定宽高限制内保持纵横比。

算法逻辑:

  1. 若原图宽高均小于目标最大值,则保持原尺寸。
  2. 若原图宽高等比大于目标限制,优先将宽度缩减至最大允许值,高度按同比例调整。
  3. 否则,优先将高度缩减至最大允许值,宽度按同比例调整。
public static Size GetNewSize(int maxWidth, int maxHeight, int imageOriginalWidth, int imageOriginalHeight)
{
    double w = 0.0;
    double h = 0.0;
    double sw = Convert.ToDouble(imageOriginalWidth);
    double sh = Convert.ToDouble(imageOriginalHeight);
    double mw = Convert.ToDouble(maxWidth);
    double mh = Convert.ToDouble(maxHeight);
    
    if (sw < mw && sh < mh)
    {
        w = sw;
        h = sh;
    }
    else if ((sw / sh) > (mw / mh))
    {
        w = maxWidth;
        h = (w * sh) / sw;
    }
    else
    {
        h = maxHeight;
        w = (h * sw) / sh;
    }
    return new Size(Convert.ToInt32(w), Convert.ToInt32(h));
}

第2步:生成指定尺寸的缩略图

目的:获取高质量的缩放后图像对象,支持直接从文件或 Stream 输入。

关键参数设置:

  • CompositingQuality = HighQuality
  • InterpolationMode = HighQualityBicubic:使用双三次插值法,虽然计算量稍大,但效果优于默认的双线性插值。
  • SmoothingMode = HighQuality:消除锯齿。
public static System.Drawing.Image GetThumbNailImage(System.Drawing.Image originalImage, int thumMaxWidth, int thumMaxHeight)
{
    Size thumRealSize = Size.Empty;
    System.Drawing.Image newImage = originalImage;
    Graphics graphics = null;
    try
    {
        thumRealSize = GetNewSize(thumMaxWidth, thumMaxHeight, originalImage.Width, originalImage.Height);
        newImage = new Bitmap(thumRealSize.Width, thumRealSize.Height);
        graphics = Graphics.FromImage(newImage);
        graphics.CompositingQuality = CompositingQuality.HighQuality;
        graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        graphics.SmoothingMode = SmoothingMode.HighQuality;
        graphics.Clear(Color.Transparent);
        graphics.DrawImage(originalImage, new Rectangle(0, 0, thumRealSize.Width, thumRealSize.Height), new Rectangle(0, 0, originalImage.Width, originalImage.Height), GraphicsUnit.Pixel);
    }
    catch { }
    finally
    {
        if (graphics != null){
            graphics.Dispose();
            graphics = null;
        }
    }
    return newImage;
}

第3步:添加图片水印

目的:在原始图片右下角叠加 Logo 或版权图片。

位置计算:通常放置在距右侧和底部各 10 像素的位置,以避免贴边过于紧凑。

public static void CreateImageWaterMark(string imageFile, System.Drawing.Image waterImage)
{
    // 校验文件存在性
    if (string.IsNullOrEmpty(imageFile) || !File.Exists(imageFile) || waterImage == null) return;
    
    // 防止水印比原图大
    System.Drawing.Image originalImage = System.Drawing.Image.FromFile(imageFile);
    if (originalImage.Width - 10 < waterImage.Width || originalImage.Height - 10 < waterImage.Height)
    {
        originalImage.Dispose();
        return;
    }
    
    Graphics graphics = Graphics.FromImage(originalImage);
    // 计算右下角坐标
    int x = originalImage.Width - waterImage.Width - 10;
    int y = originalImage.Height - waterImage.Height - 10;
    
    graphics.DrawImage(waterImage, new Rectangle(x, y, waterImage.Width, waterImage.Height));
    graphics.Dispose();
    
    // 重新保存,因直接修改 File 可能锁定,建议先写入 MemoryStream
    using (MemoryStream stream = new MemoryStream())
    {
        originalImage.Save(stream, ImageFormat.Jpeg);
        // 此时 originalImage 已不再需要,可立即释放
        originalImage.Dispose();
        
        System.Drawing.Image imageWithWater = System.Drawing.Image.FromStream(stream);
        imageWithWater.Save(imageFile); // 覆盖或另存为新文件
        imageWithWater.Dispose();
    }
}

第4步:添加文字水印

目的:添加半透明文本,如“内部使用”、“样本”等。

样式优化:使用 ClearTypeGridFit 使文字边缘清晰,配合 Alpha 通道控制透明度。

public static void CreateTextWaterMark(string imageFile, string waterText)
{
    if (string.IsNullOrEmpty(imageFile) || string.IsNullOrEmpty(waterText) || !File.Exists(imageFile)) return;
    
    System.Drawing.Image originalImage = System.Drawing.Image.FromFile(imageFile);
    Graphics graphics = Graphics.FromImage(originalImage);
    
    // 高质量渲染设置
    graphics.SmoothingMode = SmoothingMode.HighQuality;
    graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
    graphics.CompositingQuality = CompositingQuality.HighQuality;
    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
    
    // 定义半透明颜色 (A=153, R=255, G=255, B=255)
    SolidBrush brush = new SolidBrush(Color.FromArgb(153, 255, 255, 255));
    Font waterTextFont = new Font("Arial", 16, FontStyle.Regular);
    
    SizeF waterTextSize = graphics.MeasureString(waterText, waterTextFont);
    float x = (float)originalImage.Width - waterTextSize.Width - 10F;
    float y = (float)originalImage.Height - waterTextSize.Height - 10F;
    
    graphics.DrawString(waterText, waterTextFont, brush, x, y);
    
    // 资源清理
    graphics.Dispose();
    brush.Dispose();
    
    using (MemoryStream stream = new MemoryStream())
    {
        originalImage.Save(stream, ImageFormat.Jpeg);
        originalImage.Dispose();
        System.Drawing.Image imageWithWater = System.Drawing.Image.FromStream(stream);
        imageWithWater.Save(imageFile);
        imageWithWater.Dispose();
    }
}

完成判断

1. 缩略图检查:调用 GetThumbNailImage 后返回的 Image 对象宽高应符合 maxWidth/maxHeight 限制且未变形。
2. 水印位置检查:生成的图片右下角可见水印,且与原图保持固定间距(如代码中的 10px)。
3. 资源完整性:运行过程中不出现 GDI+ "OutOfMemory" 错误,说明 Dispose 调用正确,无内存泄漏。

排错与维护

1. 字体缺失问题
如果服务器环境未安装示例中的 “Arial” 字体,文字渲染会失败或使用回退字体导致布局偏移。建议使用系统通用字体如 “SimSun” 或 “微软雅黑”,并预先检测 FontFamily 是否存在。

2. 高并发下的资源锁死
直接对磁盘文件使用 Image.FromFile() 会锁定该文件,导致后续无法删除或移动。最佳实践是读取文件到 FileStream 或 MemoryStream 中再转为 Image 对象,如 ResizeImage 方法所示。

总结

以上便是关于 C# 利用 System.Drawing 实现图片水印添加与缩略图生成的详细内容,更多关于 C# 图形处理、ASP.NET 文件上传优化的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表