项目地址:github.com/lovell/shar…
信息核实时间:2026-10-31(Asia/Shanghai)。Star、Fork、许可证、最近版本来自 GitHub API 和 Release 页;参数含义来自项目自带的类型声明(lib/index.d.ts)和官方文档 sharp.pixelplumbing.com 。
实测是笔者在一台 8 核 Xeon、Node 20 的 Linux 机器上跑的,sharp 0.35.5,每项只跑了一次,耗时只能当量级参考。
测试图片:大都会艺术博物馆(The Met)藏品 “One of a Pair of Gold Earrings” 的公开图片,CC0,经 Wikimedia Commons 获取(文件名 One_of_a_Pair_of_Gold_Earrings_MET_DP30244.jpg,1833×2013)。
一、它是什么
sharp 是 Node.js 的图片处理库,底层是 C 写的 libvips。项目描述里写的是把大图转换成 JPEG、PNG、WebP、AVIF、TIFF 等常用格式,主打速度和内存占用低。
核实时的项目数据:
- Star 约 3.28 万,Fork 约 1446;
- 许可证 Apache-2.0;
- 最近一次推送 2026-10-29,最新正式版 v0.35.5(2026-09-27 发布)。
做电商的开发者用它的场景很具体:商家上传一张原图,服务端要出主图、缩略图、细节图、不同平台要求的尺寸。这类活交给 sharp 很常见。
我这次专门挑饰品来试,原因是饰品图有几个特点:主体细、有大量金属高光、颜色差一点就是客诉。批处理脚本在这类图上更容易出问题。
二、安装
npm i sharp
npm 会按平台下载预编译的二进制,我这台 x64 Linux 上装完直接能用,不需要自己编译 libvips。用 Docker 多阶段构建、或者在 Mac 上装好再拷到 Linux 服务器时,要注意二进制和平台对得上,官方文档有专门的安装说明。
三、实测 1:方形主图,三种裁剪方式差在哪
测试图是一只金耳环,竖构图,上面有一个细细的耳钩环。目标是 800×800 的方形主图。
const sharp = require('sharp');
// 留白:整件入画,空出来的地方补白
await sharp('ear1.jpg')
.resize(800, 800, { fit: 'contain', background: '#ffffff' })
.jpeg({ quality: 85, mozjpeg: true })
.toFile('out-contain.jpg');
// 裁切:铺满方形,多出来的部分裁掉
for (const [name, position] of [
['centre', 'centre'],
['attention', sharp.strategy.attention],
['entropy', sharp.strategy.entropy],
]) {
const info = await sharp('ear1.jpg')
.resize(800, 800, { fit: 'cover', position })
.jpeg({ quality: 85, mozjpeg: true })
.toFile(`out-cover-${name}.jpg`);
console.log(name, info.cropOffsetTop); // 智能裁剪时会返回裁剪偏移
}
结果:
- contain 留白:耳环完整,但原图背景是浅灰,两侧补的白边和灰背景接缝很明显。用在主图上,要么先抠成白底再 contain,要么 background 设成接近原背景的颜色。
- cover 居中:上下各裁掉 39 像素左右,耳钩环顶端已经贴到画面边缘。
- cover + attention:裁剪偏移 cropOffsetTop 为 -79,也就是多出来的 79 像素全从上方裁掉,耳钩环的顶端被切掉了。
- cover + entropy:偏移 -40,结果和居中一样。
attention 在类型声明里的说明是找“亮度频率最高、色彩饱和度最高、有肤色”的区域。耳环下半部分颗粒多、高光多,它就往那边靠,上面那根细细的耳钩对它来说不重要。对饰品来说,这恰好是最不能切的部分。
我的结论是:饰品主图别直接用 cover 的智能裁剪,用 contain 加合适的背景色,或者先抠图再 contain。智能裁剪更适合本来就有大面积背景的场景图。
四、实测 2:细节图从哪一张裁
饰品详情里少不了微距细节:金珠颗粒、焊接、镶口。批处理里一个常见写法,是先把原图缩到主图尺寸,后面所有操作都基于这张小图,包括裁细节。
// 从原图直接裁 400×400
await sharp('ear1.jpg')
.extract({ left: 911, top: 813, width: 400, height: 400 })
.toFile('detail-fullres.png');
// 先缩到 800 高,再按比例裁同一区域,放大回 400
const small = await sharp('ear1.jpg').resize({ height: 800 }).toBuffer();
await sharp(small)
.extract({ left: 362, top: 323, width: 159, height: 159 })
.resize(400, 400, { kernel: 'lanczos3' })
.toFile('detail-from800.png');
同一块区域,先缩小再裁只剩 159×159 像素可用,放大回 400 以后金珠颗粒的边缘全糊了。原图裁出来的颗粒和焊缝都很清楚。
写管道时把“原图”保留到最后,细节图一律从原图取,这个改动几乎没有成本。
五、实测 3:颜色和 ICC 配置文件
金色、玫瑰金这类颜色 SKU,最怕处理后颜色变了。现在很多手机拍出来的照片带 Display P3 配置文件,如果处理时把配置文件扔掉又不做转换,颜色会偏。
我把测试图转成带 P3 配置文件的版本(480 字节的 profile),再分别处理,取耳环上同一块 40×40 金色区域的平均 RGB:
- sRGB 原图缩放后:154.4 / 128.5 / 85.2
- 带 P3 的图用 sharp 默认参数处理:153.9 / 128.6 / 84.8(输出不带配置文件)
- 带 P3 的图用 Pillow 直接缩放、不做色彩转换:150.4 / 129.5 / 90.8
sharp 默认会把图转换到 sRGB 再去掉元数据,所以颜色基本保住了。直接丢掉配置文件不转换的那一版,蓝色通道高了 5 左右,金色明显发灰发冷。如果确实需要保留原配置文件,可以用 keepIccProfile(),我测了输出文件里配置文件还在。
这一点对“颜色款”SKU 很重要:同一个管道里如果混用了不同的库,要确认每一步都处理了色彩配置文件。
六、格式和速度
同一张 800×800 留白图,输出三种格式:
- JPEG q80(mozjpeg):34510 字节,50ms
- WebP q80:27682 字节,59ms
- AVIF q50:14847 字节,985ms
AVIF 体积最小,但编码慢了一个数量级。自建站前台可以用 WebP 或 AVIF 省流量;往国内电商平台后台上传,一般还是按平台要求出 JPG 或 PNG,具体以各平台当前规范为准。
七、适合谁,局限在哪
适合:
- 自建站、跨境站需要服务端批量出多尺寸商品图的 Node.js 项目;
- 给运营做内部小工具,统一主图尺寸、背景、格式;
- 已经有抠图结果(PNG 透明图),需要批量合成白底、加边距、出缩略图。
局限:
- 它是图片处理库,不做抠图、不做修图,白底图要先用别的方式把背景去掉;
- 智能裁剪按画面特征找重点,不懂“耳钩不能切”这种业务规则,饰品、细长商品要自己控制;
- 依赖原生二进制,跨平台部署要按文档处理;
- AVIF 编码慢,大批量时要算好时间。
我这次的实测只有一张图、每项跑一次,数字只说明量级和方向。真正上生产前,建议用自己店里的商品图各跑一批。
项目地址:github.com/lovell/shar… 测试图片:The Met, One of a Pair of Gold Earrings,CC0,via Wikimedia Commons