C#利用System.Drawing实现图片压缩与裁剪
在Web开发中,为了避免用户上传大尺寸图片导致存储压力或加载缓慢,通常需要在服务端进行自动压缩和裁剪。本文将展示如何使用C#的System.Drawing命名空间,通过读取图像、创建Graphics对象以及操作Bitmap,实现高效的图片缩放(压缩)与局部裁剪功能。
图片压缩:生成指定尺寸的缩略图
图片压缩的核心逻辑是创建一个新的、指定宽高的 Bitmap 对象,并将原图内容绘制到这个新对象上。这不仅能减小文件的物理存储大小,还能统一前端展示的尺寸规范。
第1步:读取原始图片文件
首先需要通过 Image.FromFile 方法将本地磁盘上的图片加载到内存中的 Image 对象里。Image 是 System.Drawing 命名空间下用于处理图像数据的基类。
// 输入:图片文件的全路径名称
// 返回:Image 对象
public Image ResourceImage = Image.FromFile(ImageFilePathAndName);
关键属性与方法说明:
Size/Width/Height:获取图像的像素宽高。PixelFormat:获取图像的像素格式(如32位ARGB)。Save(String fileName):将图像保存为指定的文件格式。
第2步:绘制缩略图
创建一个新的 Bitmap 实例作为画布,并利用 Graphics 对象将原图映射到新画布上。此步骤会自动执行线性插值算法以调整图像分辨率。
/// <summary>
/// 生成缩略图重载方法1,返回缩略图的Image对象
/// </summary>
/// <param name="Width">缩略图的宽度</param>
/// <param name="Height">缩略图的高度</param>
/// <returns>缩略图的Image对象</returns>
public Image GetReducedImage(int Width, int Height)
{
try
{
// 1. 初始化目标位图(例如 200x200),注意设定 PixelFormat
// Format32bppArgb 支持透明通道
Bitmap bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
// 2. 从目标位图创建 Graphics 绘图图面
Graphics graphics = Graphics.FromImage(bitmap);
// 3. 清除整个绘图面并以透明背景色填充
// 这对于保留PNG透明度至关重要
graphics.Clear(Color.Transparent);
// 4. 在原图与新图之间建立映射:
// 将 ResourceImage 按 (0,0) 到 (Width, Height) 的矩形区域绘制到 bitmap
graphics.DrawImage(ResourceImage, new Rectangle(0, 0, Width, Height));
return bitmap;
}
catch (Exception e)
{
// 实际项目中建议记录日志而非直接返回 null
ErrMessage = e.Message;
return null;
}
}
第3步:保存结果
调用 GetReducedImage 获取 Image 对象后(记为 iImage),需将其持久化保存。指定 ImageFormat.Jpeg 可在视觉上大幅降低文件体积。
iImage.Save(pathAndName, System.Drawing.Imaging.ImageFormat.Jpeg);
经测试,一张 101KB 的图片经过上述尺寸压缩后,文件大小可降至约 57KB(具体取决于原图尺寸比例与设定的输出分辨率)。
图片裁剪:截取指定区域
裁剪操作比压缩更精确,它通过定义“源矩形”(Source Rect)和“目标矩形”(Dest Rect),将原图的特定像素块复制到新图中。常用于头像提取、海报局部放大等场景。
/// <summary>
/// 截取图片方法
/// </summary>
/// <param name="url">原图地址</param>
/// <param name="beginX">开始位置-X坐标</param>
/// <param name="beginY">开始位置-Y坐标</param>
/// <param name="getX">截取宽度</param>
/// <param name="getY">截取高度</param>
/// <param name="fileName">文件名</param>
/// <param name="savePath">保存路径</param>
/// <param name="fileExt">后缀名(png/bmp/gif)</param>
/// <returns>成功返回新文件路径,失败返回错误信息</returns>
public static string CutImage(string url, int beginX, int beginY, int getX, int getY, string fileName, string savePath, string fileExt)
{
// 1. 基础合法性校验
if ((beginX < getX) && (beginY < getY))
{
// 2. 加载原图
Bitmap bitmap = new Bitmap(url);
// 3. 检查裁剪范围是否超出原图边界
if (((beginX + getX) <= bitmap.Width) && ((beginY + getY) <= bitmap.Height))
{
// 4. 创建目标空图
Bitmap destBitmap = new Bitmap(getX, getY);
// 定义绘制目标区域(填满整个 destBitmap)
Rectangle destRect = new Rectangle(0, 0, getX, getY);
// 定义源图区域(要截取的部分)
Rectangle srcRect = new Rectangle(beginX, beginY, getX, getY);
using (Graphics g = Graphics.FromImage(destBitmap))
{
// 核心指令:将 srcRect 的内容拉伸/平铺绘制到 destRect
// GraphicsUnit.Pixel 确保按像素点对点计算
g.DrawImage(bitmap, destRect, srcRect, GraphicsUnit.Pixel);
}
// 5. 根据扩展名确定保存格式
ImageFormat format = ImageFormat.Png; // 默认 PNG
switch (fileExt.ToLower())
{
case "png": format = ImageFormat.Png; break;
case "bmp": format = ImageFormat.Bmp; break;
case "gif": format = ImageFormat.Gif; break;
// JPEG 不支持透明背景,如需透明请慎用 JPG
}
// 6. 保存文件并返回路径
string fullPath = savePath + "\\" + fileName;
destBitmap.Save(fullPath, format);
return fullPath;
}
else
{
return "截取范围超出图片范围";
}
}
else
{
return "参数错误:请确认 beginX < getWidth && beginY < getHeight";
}
}
技术要点解析:
- Rectangle结构体:
Rectangle(x, y, width, height)是GDI+绘图的基础,它定义了左上角起点及矩形的宽高。 - DrawImage 的重载:代码中使用的
DrawImage(Image, Rectangle, Rectangle, GraphicsUnit)是裁剪的关键。第一个 Rectangle 是“去哪里画”,第二个 Rectangle 是“从哪拿数据”。 - 资源释放:
Bitmap和Graphics均实现了IDisposable接口。在生产环境中,建议使用using语句包裹这些对象,以防止内存泄漏和非托管资源占用。
注意事项与排错
- GDI+ 锁定问题:如果通过
FileStream打开文件而不是直接用路径字符串构造Bitmap,在保存前必须关闭流,否则 GDI+ 会因为文件被占用而抛出“一般性错误”。 - 服务器环境依赖:System.Drawing 基于 Windows GDI 引擎。如果在 Linux/Docker 环境下运行,通常需要安装 libgdiplus,或者考虑迁移至ImageSharp等非依赖原生库的解决方案。
- 性能消耗:
System.Drawing适合少量同步请求。高并发场景下建议评估其线程阻塞影响,或改用异步图像处理中间件。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













