C#利用 System.Drawing 实现图片水印与缩略图处理
在 Web 开发或桌面应用中,经常需要对用户上传的图片进行统一处理,包括自动裁剪缩略图和添加品牌标识。基于 .NET Framework 中的 System.Drawing 库,我们可以编写一个通用的图像处理工具类,实现等比例缩略图生成、图片叠层水印及半透明文字水印功能。
实现思路与核心逻辑
所有操作均围绕 System.Drawing 命名空间展开。核心步骤包括:加载源图像 -> 创建 Graphics 实例 -> 执行绘图指令(绘制背景图、水印图或文字) -> 保存结果。为确保性能,必须严格管理非托管资源的释放,特别是在捕获异常时仍需执行清理工作。
准备条件
项目需引用以下 DLL:System.Drawing.dllSystem.Web.dll (若在 ASP.NET 环境下运行以获取服务器路径)
操作步骤
第1步:计算等比例缩小后的尺寸
目的:防止图片变形,确保缩略图在指定宽高限制内保持纵横比。
算法逻辑:
- 若原图宽高均小于目标最大值,则保持原尺寸。
- 若原图宽高等比大于目标限制,优先将宽度缩减至最大允许值,高度按同比例调整。
- 否则,优先将高度缩减至最大允许值,宽度按同比例调整。
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 = HighQualityInterpolationMode = 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 文件上传优化的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













