最直接控制jpeg质量的方式是用image.save配合encoderparameters设置encoder.quality(0–100),需显式获取jpeg编码器,且参数数组只能含一个encoderparameter;imagesharp更安全跨平台,支持quality属性直设及异步流式处理。

用 Image.Save 配合 EncoderParameters 控制 JPEG 质量最直接
多数场景下,你不是要“无损压缩”,而是降低文件体积同时保持可接受的视觉质量。C# 的 System.Drawing(或 System.Drawing.Common)里,Image.Save 方法配合 JPEG 编码器参数是最常用、最可控的方式。
关键点在于:JPEG 压缩质量由 EncoderParameter 的 Encoder.Quality 决定,取值 0–100(注意:不是百分比意义上的“越高越好”,而是编码器内部量化表强度,100 几乎无损,30–70 是常见平衡区间)。
- 必须显式指定
ImageCodecInfo获取 JPEG 编码器,不能只传".jpg"后缀 -
EncoderParameters必须包含且仅包含一个EncoderParameter,否则可能静默失败或抛ArgumentException - .NET 6+ 在 Linux/macOS 上需安装
libgdiplus,否则Image.Save会抛DllNotFoundException
var image = Image.FromFile("input.jpg");
var jpegEncoder = GetJpegEncoder(); // 需自行实现查找 encoder 的方法
var encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, 75L); // 注意是 long 类型
image.Save("output.jpg", jpegEncoder, encoderParams);
image.Dispose();
用 ImageSharp 替代 System.Drawing 更安全、跨平台
如果你在 .NET Core / .NET 5+ 项目中使用 System.Drawing,尤其部署到容器或非 Windows 环境,大概率会遇到图像操作失败、内存泄漏或线程不安全问题。此时应优先考虑 ImageSharp —— 它纯 C# 实现、无本地依赖、支持异步、线程安全。
它的压缩逻辑更贴近现代需求:按目标文件大小(而非固定质量值)自动调节,或精确控制输出尺寸 + 质量双约束。
- 安装包:
ImageSharp(主库) +ImageSharp.Drawing(如需文字/图形叠加) - 默认 JPEG 输出使用
JpegEncoder,Quality属性直接设为 int(0–100),无需封装EncoderParameter - 若需限制最终文件大小(比如强制 ≤ 200KB),得手动循环尝试不同
Quality值并检查输出流长度 —— 它不内置“按大小压缩”逻辑
using var image = await Image.LoadAsync("input.jpg");
var encoder = new JpegEncoder { Quality = 70 };
using var stream = new MemoryStream();
await image.SaveAsync(stream, encoder);
// stream.Length 即压缩后字节数
压缩 PNG 时别误用 JPEG 参数 —— 格式与编码器必须匹配
把一张 PNG 图片用 JPEG 编码器保存,虽然能生成文件,但本质是“转格式 + 压缩”,会丢失透明通道、引入压缩伪影,且无法还原原始 PNG。真要压缩 PNG,目标是减少调色板大小、关闭冗余 chunk、启用 zlib 压缩级别优化,而不是调 Quality。
System.Drawing 对 PNG 基本不提供压缩控制(Encoder.Quality 对 PNG 无效);ImageSharp 则通过 PngEncoder 的 CompressionLevel(0–10,默认 6)和 BitDepth(对索引色 PNG 可降为 4 或 2)来影响体积。
- 不要对 PNG 设置
Quality(ImageSharp会忽略,System.Drawing可能报错或静默失效) - 若源图是真彩色且含 alpha,强行转为 8-bit 索引色 PNG 会导致明显色带,需先做调色板量化(
ImageSharp提供Quantize方法) - 想彻底减小 PNG 体积,工具链上建议用
pngcrush或oxipng后处理 —— 这些比纯托管代码更激进
大图压缩卡死?注意内存峰值和流式处理边界
一张 8000×6000 的 TIFF 加载成 Bitmap,内存占用轻松超 200MB(未压缩位图 = 宽 × 高 × 每像素字节数)。直接 Save 不仅慢,还可能触发 OutOfMemoryException,尤其在 32 位进程或内存受限容器中。
- 优先用
ImageSharp的Configuration.Default.MemoryAllocator绑定ArrayPoolMemoryAllocator,减少 GC 压力 - 避免一次性加载再保存:对上传流,直接
Image.LoadAsync(inputStream);对磁盘文件,用FileStream+FileShare.Read防止锁死 -
System.Drawing的Bitmap构造函数若传入大尺寸,会立即分配内存;可用Image.GetThumbnailImage先缩略(但质量差,仅适合预览)
真正需要高压缩比又兼顾性能的场景,绕不开原生库(如 libvips 绑定)或服务化处理(如调用 Cloudflare Images、Imgix API)。纯 C# 托管方案有明确物理上限。











