返回文章列表

Squoosh CLI、imagemin、sharp 还是 PiPic,2026 年命令行压图工具体检

PiPic 团队
Agents
pipic ./img -o ./out --json
{"file":"…/img/broken.jpg","status":"error",…}
✗ exit 1

想在终端里压图,去问人,答案翻来覆去就那几个。Squoosh 的 CLI、imagemin、 sharp,讲究一点的会让你直接上 pngquant 这类单格式编码器。这些推荐大多是 2023 年之前传下来的。所以 2026 年 8 月 7 日下午,我们把它们全部重新装了一遍。 一台普通开发机,macOS,Apple Silicon,Node 22.14,也就是现在的 LTS。测试 文件夹里放四个文件,一张 PNG,一张 JPEG,一张已经优化过一轮的 JPEG,再加 12 个字节的纯文本,改名叫 broken.jpg。真实项目的图片目录里,总会混进这种 东西。

动手之前先查了一圈 npm 上的发版时间,底子已经能看出个大概。

latest release per package · npm registry · checked 2026-08-07

@squoosh/cli 0.7.3 Jan 2023 "no longer maintained" imagemin 9.0.1 Mar 2025 imagemin-cli 8.0.0 May 2024 imagemin-mozjpeg 10.0.0 Dec 2021 imagemin-webp 8.0.0 Jan 2023 sharp 0.35.3 Jul 2026 sharp-cli 5.2.0 Jun 2025

Squoosh CLI 第一个上场,直接崩了

先试 @squoosh/cli,因为它被推荐得最多。它是 Squoosh 的命令行版本。网页版 是 Google 家的,一直有人维护,确实好用。CLI 就是另一回事了,最后一版停在 2023 年 1 月,readme 里自己写着:"Project no longer maintained."(项目不再 维护。)

装的时候一切正常,命令也能跑起来。真让它压图,当场就崩。它加载 WebAssembly 的方式是拿 fetch() 去取一个本地文件路径,今天的 Node 不认这种写法。

npx @squoosh/cli --oxipng '{}' -d out photo.png · Node 22.14

code: 'ERR_INVALID_URL', input: '…/@squoosh/lib/build/imagequant_node-a4aafbae.wasm' — exit 1

换 JPEG 再试,同一个地方,同样的死法。也就是说,今天装上这个工具,一张图 都压不出来。教程还在推荐它,拿这些教程训练出来的模型也还在推荐它。没人去 更正这些建议,因为项目里已经没人了。

imagemin 把图压好了,也把假文件放过去了

imagemin 的第一印象好一些。核心包今年 3 月还发过版,压真图也确实没问题, 三个文件丢进去,回一句 "3 images minified",收工。

但过程里有两件事让人不舒服。头一件是安装。装四个包,蹦出来五条 deprecated 警告,npm audit 在这个刚建好的空项目里数出 37 个漏洞,其中 1 个 critical。原因不难找。压缩这活全由各格式的插件来干,而插件都老了。JPEG 插件 停在 2021 年 12 月,WebP 停在 2023 年 1 月,AVIF 干脆没有官方插件。

第二件是那个假文件。

npx imagemin broken.jpg --plugin=mozjpeg · 12 bytes of text
  • Minifying images 1 image minified — exit 0

它把 12 个字节的文本原样拷进输出目录,算成一张压缩成功的图,退出码 0。人 扫一眼就能看出不对,脚本看不出来。agent 读到 "1 image minified, exit 0", 只会汇报一切正常,接着干下一件事。

sharp 本身没毛病,坑在别的地方

sharp 的状态是全场最好的。今年 7 月还在发版,撑了 npm 图像生态十年,论维护 谁也比不过它。

但 sharp 不管"压一个文件夹"这件事。它是个库,脚本得你自己写。我们照大家 平时的写法写了几行,全用默认参数,跑出来是这样。

node compress.mjs · sharp, default .jpeg() quality

photo.jpg 88556 -> 38879 smaller photo-optimized.jpg 26659 -> 32050 LARGER — exit 0

新导出的 JPEG 压得很好。已经优化过的那张,出来反而大了 20%,脚本还是照存 不误,因为我们没写"变大就别存"这个判断。这不怪 sharp。quality 给多少、 哪些文件该跳过、变大了怎么办、结果怎么汇报,它全都留给写脚本的人。而写脚本 的,通常是晚上六点急着下班的你,或者你的 agent。agent 即兴编的正是这种脚本, 我们在给你的 coding agent 一个图片压缩器里 写过,这回亲眼看它翻了车。

有个数字对我们不利

这个数字得摆到台面上。默认参数下,imagemin 的 mozjpeg 压出了全场最小的 JPEG,26,659 字节,pipic 压出来是 31,774。不过各家默认的质量档不一样,光比 字节数说明不了什么,得配上画质评估才有意义,而画质评估我们没做。这篇比的是 行为,不排压缩率的名次。只是这个数字不该藏着。

顺带把单格式编码器也交代了。pngquantoxipngcjpegcwebpavifenc 都是好东西,上面这些工具包的就是它们。只跟一种格式打交道的话, 用它们完全够(AVIF 值不值得上,AVIF 页面有专门说明)。要 处理四种格式,那就是四次安装、四套参数方言,外加一个从此归你维护的封装脚本。

同一个文件夹,轮到 pipic

一模一样的目录交给 pipic CLI,坏文件也没拿走。

$ pipic ./img -o ./out --json          # paths shortened
{"file":"…/img/broken.jpg","status":"error","before":12,"after":0,"saved":0,"error":"UPSTREAM_ERROR: Compression service error"}
{"file":"…/img/icon.png","status":"ok","before":13897,"after":7449,"saved":6448}
{"file":"…/img/photo-optimized.jpg","status":"skipped","before":26659,"after":26659,"saved":0,"error":"not smaller — copied as-is"}
{"file":"…/img/photo.jpg","status":"ok","before":88556,"after":31774,"saved":56782}
{"file":"…/img/photo.png","status":"ok","before":302850,"after":104288,"saved":198562}
— exit 1

前面几家答错的题,可以对着这份输出一道道看。文本文件记成了 error,带着 原因,整次运行退出码 1,脚本一看就知道有东西没成。已优化的 JPEG 记成 skipped,原文件没动,理由就写在那一行里,没有被悄悄存大 20%。剩下每张图 各占一行 JSON,压缩前后的字节数都在,脚本直接按字段分支就行。

代价也得说清楚。这篇里其他工具都在你自己机器上跑,pipic 是个服务。文件要 上传,在内存里压完马上删掉,不落盘不存储,但确实出了你的电脑。CLI 要登录, 每月免费 100 张,Pro 每月 $9,5,000 张。网页压缩器不受这些影响,一直 免费、不限量、不用注册。如果你的规矩是"图不许出这台机器",那就用本地工具, 这条规矩比这篇文章里所有结论都大。

登录换来的东西也明摆着,就是前面每条发现的反面。没有会烂掉的 wasm 加载器, 不用自己攒插件,不用即兴决定 quality,输出是自动化真能靠住的。

我们还单独做了一组按格式分层的基准测试,12 个公开许可输入覆盖 PNG、JPEG、WebP 和 AVIF。方法、72 次原始运行、中位数、保留的失败记录和视觉检查 都已公开。这组数据比较 PiPic Agent CLI 1.0.6 与 sharp 0.35.3。上面的文件夹测试 关注工具的操作行为,不提供压缩率排名。

这个实验你自己也能做

整套方法一个下午就能走完,也不用信我们的数字。先看看项目里还有没有人。

npm view @squoosh/cli time.modified

然后建个文件夹,放一张已经优化过的图,再放一个坏文件,拿你想用的工具跑一遍。 干净的图在哪家都压得好,看不出差别。工具的判断力,全在碰到不干净的文件那 一刻。而哪个文件夹能永远干净呢。

拿你自己的图片试试
免费,无需注册,绝不留存。
开始压缩