先说结论:搜「图片批量压缩」排在最前面的一批 squoosh-cli 教程,可以关掉了。npm 上 @squoosh/cli 页面顶部挂着「Project no longer maintained」,版本停在 0.7.3,官方 2023 年 1 月就提交了删除 CLI 与 libsquoosh 的改动。命令行批量压图,我这次用的是 sharp 做主流水线、oxipng 专啃 PNG 无损,下面把三条路线的取舍和实测数字摊开讲。
需求背景:这次到底要压什么
起因很普通:一批 1600×1200 的 PNG,6 张就 10.17 MB,要往网页里塞,而且以后每周都要出,靠手点迟早出错。
所以约束先写死:一次吃一个目录,参数显式写进脚本而不是手点,体积掉在哪一步(降质量还是降尺寸)要说得清。我原本以为这是「找个好用的压缩器」的问题,后来发现其实是「找个还能维护下去的依赖」的问题——二手教程最大的坑不是参数写错,是它推荐的东西早就不维护了。
方案 A:还在被大量教程推荐的 squoosh-cli
squoosh-cli 确实好用过:一条命令吃一堆文件,--mozjpeg/--webp/--avif/--oxipng 任选,还能用 --wp2 auto 在给定 Butteraugli 目标下自动压到不能再压。
问题出在维护状态,两处官方口径一致:npm 包页顶部挂着「Project no longer maintained」,原话提到「due to a few people leaving the team… no longer actively maintained」,并注明 squoosh.app 网页版继续维护;仓库 README 里 2023 年 1 月那条提交就是删除 CLI 与 libsquoosh。
这里有个容易混淆的点:网页版还在,命令行没了。squoosh.app 依然是「图片不出本机、浏览器里跑 codec」的在线工具,压三五张完全够用。判断标准就一条:偶尔几张用网页版,每周几百张就别把它写进自动化脚本。
方案 B:sharp,把压缩做成一条流水线
sharp 的定位是「把大图转成适合 web 的 JPEG / PNG / WebP / GIF / AVIF」,底层 libvips。官方 README 写得很实在:Node.js 要 >= 20.9.0,缩放通常比最快的 ImageMagick 设置快 4 到 5 倍,Apache-2.0,多数系统装完即用。
实测环境:sharp 0.35.5、Node.js 22.22.2、Windows 8 逻辑核,输入 6 张 1600×1200 的 PNG,合计 10.17 MB。先说清楚:这几张是用脚本生成的渐变、噪声和色带图,不是真实照片,百分比只用于同批图的横向比较。
| 输出设置 | 体积 | 占源 | 耗时 |
|---|---|---|---|
| JPEG q82 | 3.50 MB | 34.4% | 377 ms |
| JPEG mozjpeg q82 | 2.92 MB | 28.7% | 3331 ms |
| JPEG mozjpeg q60 | 1.62 MB | 15.9% | 2230 ms |
| WebP q80 | 3.59 MB | 35.3% | 2224 ms |
| WebP q80 + 宽 1200 | 1.64 MB | 16.1% | 1398 ms |
| AVIF q50 | 2.57 MB | 25.3% | 12169 ms |
mozjpeg 在同 quality 下比默认 JPEG 小 16.6%,代价是从 377 ms 涨到 3331 ms,约 8.8 倍——值不值,看你是压一批存档还是每次构建都重跑。AVIF 比 WebP 小约 28%,耗时却是 5.5 倍。
最有意思的是倒数第二行:收到宽 1200 再压 WebP q80,和原尺寸把质量砍到 60,体积几乎一样(1.64 MB 对 1.62 MB),但缩尺寸那版反而快了 800 多毫秒。降采样丢的是眼睛本来就分辨不出的细节,降质量丢的是高频纹理上的块状噪声,同样体积前者更耐看。
真正让耗时掉下来的是并发:同批 WebP q80,并发 1 是 2148 ms,并发 4 是 774 ms,并发 6 只有 762 ms。开到 4 之后基本没收益,因为 libvips 内部本身就是多线程的。并发度别照抄核数,用固定大小的 worker 池:
const files = fs.readdirSync(SRC);
let i = 0;
const worker = async () => {
while (i < files.length) {
const f = files[i++];
await sharp(path.join(SRC, f)).webp({ quality: 80 }).toFile(path.join(OUT, f));
}
};
await Promise.all(Array.from({ length: 4 }, worker)); 再记一个坑:png({ compressionLevel: 9 }) 对这批图几乎没用,10.17 MB 压到 10.16 MB,还花了 1575 ms。源图本身已是 zlib 压过的 PNG,无损重压压不动是常态,不是工具坏了。加 palette: true 能到 48.6%,但那是有损的,照片会掉色,耗时 14979 ms 全场最慢,只适合图标和色块截图。
方案 C:oxipng,只啃 PNG 无损这一件事
如果诉求是「PNG 必须无损,一个像素都不许变」,该看 oxipng。官方定位:多线程的 PNG / APNG 无损压缩优化器,既能当命令行用也能当 Rust 库嵌进程序,MIT 许可,ImageOptim、Squoosh、FileOptimizer 内部都在用。装法:Windows 走 Releases,有工具链就 cargo install oxipng(官方写明最低 Rust 1.85.1)。
官方给的示例命令:
oxipng -o 4 --strip safe --alpha *.png -o 是优化级别 0 到 6 或 max,默认 2,官方原话是默认级别「quite fast and provides good compression」,更高级别通常更好但收益递减。--strip 删元数据,safe 只删绝不影响渲染的部分。
要单独提醒的是 --alpha:它改写全透明像素的颜色值来提升压缩率,官方推荐日常开着,同时明确标注「this is technically a lossy transformation」。开了它,oxipng 就不再是你以为的纯无损工具,做归档和合规留证的场景别开。
官方还声明它不是暴力优化器:高优化级别几乎总是优于低级别,但不保证,极少数情况下低级别反而更小。我这轮没能把它跑起来,本机没有 Rust 工具链,以上为官方 README 口径,参数请以仓库最新说明为准。
横向对比表:三条路线摆在一起
| 维度 | squoosh-cli | sharp | oxipng |
|---|---|---|---|
| 维护 | 已弃用,0.7.3 | 活跃,Apache-2.0 | 活跃,MIT |
| 擅长 | 一次试用多种 codec | 尺寸+格式+质量一条线 | PNG 无损瘦身 |
| 结论 | 网页版可用,CLI 别进自动化 | 批量主力 | PNG 归档最后一道 |
怎么选:先改尺寸,再改质量
只带走一条的话:先问这张图要显示多大,再问用什么质量压。实测里缩到 1200 宽加 WebP q80,和原尺寸配 mozjpeg q60 体积基本打平,但前者更快更好看。多数人一上来就调 quality,方向反了。
剩下的按场景分:每周固定出图要接自动化,用 sharp,并发池开 4 左右;PNG 图标要留原始像素,用 oxipng 的 -o 4 --strip safe,别加 --alpha;偶尔三五张,别装东西,网页版就够;要极致体积且能等,上 AVIF,把耗时单独算进预算。
还有个容易忽略的点:压缩改变的不只是体积,还有几何信息。长图表格分割 OCR 那篇提过「检测阶段会把宽图压缩导致坐标偏移」——压完的图若还要送去做识别或测量,缩放比例必须提前固定。批量任务的节奏同理,抖音视频下载工具那篇里那句「进度可控、失败可重试」我很认同,几千张的活儿要留一份失败清单。真要批量处理图片本身,通用验证码识别工具那篇讲的就是这条链路;传输环节想再省一点,指数数据采集 API 那篇提到的 gzip 是同一个思路。
常见问题
squoosh-cli 完全不能用了吗?
不是不能用,是别依赖。包还能装、命令还能跑,但官方 2023 年就停止维护,新系统、新 Node 版本的问题不会有修复。偶尔手动跑一次无妨,写进每周自动执行的脚本就是埋雷。
为什么无损压缩 PNG 几乎压不动?
因为源图通常已是压缩过的。我拿 PNG 重压到 compressionLevel 9,10.17 MB 只到 10.16 MB。无损优化能挤的是元数据、编码参数和滤波方式这些边角料。想掉体积就得接受有损:换 WebP / AVIF、降 quality,或者缩尺寸。
AVIF 值得全量替换 WebP 吗?
看量。实测 AVIF 比 WebP 小约 28%,耗时约 5.5 倍,并发拉满也只是 12.2 秒降到 11.0 秒,瓶颈在编码本身。适合对体积敏感、能离线慢慢跑的场景,构建流程里同步跑会拖长流水线,另外记得留回退格式。

