用 Image.Save
配合 EncoderParameters
控制 JPEG 质量
这是最常用、效果直接的压缩方式,只适用于
Jpeg格式。核心是降低质量参数(
EncoderParameter中的
Quality),数值范围 0–100,通常设为 70–85 就能在肉眼难辨失真的前提下显著减小体积。
常见错误:直接调用
image.Save("out.jpg") 不指定编码器,会用默认高质量(接近 100),根本压不动。
必须显式创建 JpegCodec并传入
EncoderParameters
Quality是
long类型,不是
int或
float,传错类型会抛
ArgumentException源图如果是 PNG 或 BMP,先用
Image.FromFile加载再保存为 JPEG,格式转换本身就会大幅降体积
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
<p>public static void SaveAsJpegWithQuality(Image source, string path, long quality = 80)
{
var jpegEncoder = GetEncoder(ImageFormat.Jpeg);
var encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, quality);
source.Save(path, jpegEncoder, encoderParams);
}</p><p>private static ImageCodecInfo GetEncoder(ImageFormat format)
{
var codecs = ImageCodecInfo.GetImageEncoders();
return codecs.FirstOrDefault(c => c.FormatID == format.Guid);
}用 Bitmap
缩放尺寸再压缩,兼顾分辨率与体积
单纯调质量参数压到很低(如 30)会导致明显模糊;而先缩小像素尺寸(比如宽高各缩到 70%),再以中等质量(75)保存,往往获得更优的清晰度/体积比。
注意:缩放不是无损操作,
Bitmap构造时若不指定插值模式,默认用低质量双线性,边缘易发虚。 务必设置
Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic缩放比例建议用
Math.Max(0.3, Math.Min(0.9, ratio))限制在合理区间,避免过度压缩或无效缩放 原始尺寸过大(如 >5000px)时,分两轮缩放(先缩到 2000px 再缩到目标)比单次缩放抗锯齿效果更好
public static Image ResizeImage(Image source, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / source.Width;
var ratioY = (double)maxHeight / source.Height;
var ratio = Math.Min(ratioX, ratioY);
<pre class='brush:php;toolbar:false;'>var newWidth = (int)(source.Width * ratio);
var newHeight = (int)(source.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
using (var g = Graphics.FromImage(newImage))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(source, 0, 0, newWidth, newHeight);
}
return newImage;}
处理 PNG 时别硬转 JPEG:用 ImageSharp
保持透明 + 有损压缩
如果原图是带 Alpha 通道的 PNG,强行用上面的 JPEG 方式会丢失透明背景,变成白底或黑底。这时应优先考虑现代库,比如
ImageSharp,它支持对 PNG 启用有损压缩(类似 JPEG 的质量控制),同时保留透明度。
常见误区:以为 PNG 只能无损,其实
ImageSharp的
PngEncoder有
CompressionLevel和实验性
PaletteQuantization,可有效控体积。 需安装 NuGet 包:
ImageSharp和
ImageSharp.Drawing
CompressionLevel是 0–11(对应 zlib 级别),设为 6 已有明显压缩效果,且编码速度可接受 若允许轻微颜色失真,启用
QuantizeAlpha = true和
MaxColors = 256可进一步减小 PNG 体积
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
<p>public static async Task SavePngWithCompression(string inputPath, string outputPath, int compressionLevel = 6)
{
using var image = await Image.LoadAsync(inputPath);
var encoder = new PngEncoder
{
CompressionLevel = (PngCompressionLevel)compressionLevel,
QuantizeAlpha = true,
MaxColors = 256
};
await image.SaveAsync(outputPath, encoder);
}批量压缩时注意内存和 GDI+ 句柄泄漏
循环处理上百张图时,仅靠
using不一定能及时释放底层 GDI+ 资源,尤其在 Windows 上容易触发
OutOfMemoryException或“参数无效”错误——这往往是句柄耗尽的表现,而非真内存不足。
关键点不在代码写法多漂亮,而在资源释放是否真正及时、彻底。
对每个Image实例,确保在
using块内完成所有操作,不要跨块复用 避免在循环里反复调用
Image.FromFile后不立即释放;改用
File.ReadAllBytes+
MemoryStream加载,可绕过部分 GDI+ 锁文件问题 在 .NET 6+ 中,优先用
ImageSharp替代
System.Drawing,它不依赖 GDI+,无句柄泄漏风险
真正麻烦的从来不是“怎么压”,而是压完几百张后程序卡死或报错——那大概率是资源没清干净,而不是算法不对。
