环企首页
sharp
概述
sharp 是 Node.js 中处理图片的高性能库,基于 libvips(最快的开源图片处理库之一),性能远优于 ImageMagick 和 Canvas。对常见图片操作(缩放、裁剪、格式转换、压缩)做了极致的优化,支持 JPEG、PNG、WebP、TIFF、GIF、SVG 等格式。
核心特点:
- 极高的处理速度(比 ImageMagick 快 4-10 倍)
- 极低的内存占用
- 流式处理(支持 pipe)
- 输出图片质量可控
安装:
npm install sharp
核心函数
sharp(input)
创建 Sharp 实例,传入图片源。
const sharp = require('sharp')
// 从文件
sharp('input.jpg')
// 从 Buffer
sharp(bufferData)
// 从 Stream
fs.createReadStream('input.jpg').pipe(sharp())
// 从 URL
const got = require('got')
got.stream(url).pipe(sharp().toFormat('jpeg'))
格式转换
.toFormat(format, options)
将图片转为指定格式。
// 转 JPEG
sharp('photo.png').toFormat('jpeg', { quality: 80 }).toFile('photo.jpg')
// 转 WebP(压缩率更高)
sharp('photo.jpg').toFormat('webp', { quality: 85 }).toFile('photo.webp')
// 转 PNG
sharp('photo.jpg').toFormat('png', { compressionLevel: 9 }).toFile('photo.png')
// 设为 WebP 输出(简写)
sharp('photo.jpg').webp({ quality: 80 }).toFile('photo.webp')
sharp('photo.jpg').jpeg({ quality: 80, progressive: true }).toFile('photo.jpg')
sharp('photo.jpg').png({ compressionLevel: 9 }).toFile('photo.png')
尺寸调整
.resize(width, height, options)
// 按宽度等比缩放(高度自动)
sharp('photo.jpg').resize(800).toFile('photo-800.jpg')
// 指定宽高(可能变形)
sharp('photo.jpg').resize(800, 600).toFile('photo-800x600.jpg')
// 等比缩放,最长边 800px
sharp('photo.jpg').resize(800, 800, {
fit: 'inside',
withoutEnlargement: true
}).toFile('photo-thumb.jpg')
// 裁剪为固定宽高(居中裁切)
sharp('photo.jpg').resize(800, 600, {
fit: 'cover',
position: 'center'
}).toFile('photo-crop.jpg')
// fit 模式说明:
// 'cover' — 填满区域,可能裁剪
// 'contain' — 保持比例,最大容纳(留白)
// 'fill' — 强制填满,可能变形
// 'inside' — 等比缩放,不超出,不裁剪,不放大
// 'outside' — 等比缩放,至少填满,可能溢出
// position(裁切/填充位置)
// 'top', 'bottom', 'left', 'right', 'center'(默认)
// 'top-left', 'bottom-right' 等组合
// 或传具体数字坐标
旋转与翻转
// 自动旋转(根据 EXIF 方向信息)
sharp('photo.jpg').rotate().toFile('photo-rotated.jpg')
// 指定角度(0-360)
sharp('photo.jpg').rotate(90).toFile('photo-rotated.jpg')
// 水平翻转
sharp('photo.jpg').flop().toFile('photo-flopped.jpg')
// 垂直翻转
sharp('photo.jpg').flip().toFile('photo-flipped.jpg')
裁剪
// 指定区域裁剪
sharp('photo.jpg')
.extract({ left: 100, top: 100, width: 400, height: 400 })
.toFile('photo-cropped.jpg')
// 从右边裁切 200px
sharp('photo.jpg')
.extract({ left: 0, top: 0, width: 1920, height: 1080 })
.toFile('photo-trimmed.jpg')
输出方式
// 输出到文件
await sharp('input.jpg').toFile('output.jpg')
// 输出到 Buffer
const buffer = await sharp('input.jpg').toBuffer()
// 指定输出格式
const buffer = await sharp('input.jpg')
.jpeg({ quality: 80 })
.toBuffer()
// 输出到 Stream
const stream = sharp('input.jpg').png().pipe(otherStream)
// 获取图片元数据(不修改)
const meta = await sharp('input.jpg').metadata()
console.log(meta)
// {
// width: 1920, height: 1080,
// format: 'jpeg', size: 102400,
// channels: 3, hasAlpha: false,
// orientation: 1, density: 72
// }
图片合成(叠加)
// 水印叠加
const watermark = await sharp('watermark.png')
.resize(100, 100)
.toBuffer()
await sharp('photo.jpg')
.composite([{
input: watermark,
top: 10,
left: 10,
blend: 'over' // 'over', 'in', 'out', 'atop', 'xor', 'add', 'saturate', 'multiply'
}])
.toFile('photo-with-watermark.jpg')
// 多图合成
await sharp('base.jpg')
.composite([
{ input: 'overlay1.png', top: 0, left: 0 },
{ input: 'overlay2.png', top: 100, left: 100 }
])
.toFile('composite.jpg')
模糊与锐化
// 高斯模糊
sharp('photo.jpg')
.blur(5)
.toFile('photo-blur.jpg')
// 锐化(适合处理缩放后的图片)
sharp('photo.jpg')
.resize(400)
.sharpen({ sigma: 1.5 })
.toFile('photo-sharp.jpg')
调整亮度和色彩
// 调整亮度(-1 到 1)
sharp('photo.jpg').modulate({ brightness: 1.2 }).toFile('photo-bright.jpg')
// 调整饱和度(-1 到 1)
sharp('photo.jpg').modulate({ saturation: 0.5 }).toFile('photo-saturate.jpg')
// 色相旋转(0-360)
sharp('photo.jpg').modulate({ hue: 30 }).toFile('photo-hue.jpg')
// 灰度
sharp('photo.jpg').grayscale().toFile('photo-gray.jpg')
// 复合操作
sharp('photo.jpg')
.resize(800, 600, { fit: 'cover' })
.rotate()
.jpeg({ quality: 85, progressive: true })
.toFile('photo-processed.jpg')
常用场景
const sharp = require('sharp')
const fs = require('fs')
// 生成缩略图
const generateThumb = async (input, output) => {
await sharp(input)
.resize(200, 200, { fit: 'cover', position: 'center' })
.jpeg({ quality: 80 })
.toFile(output)
}
// 生成响应式图片
const generateResponsive = async (input) => {
const widths = [320, 640, 960, 1280]
const sizes = await Promise.all(
widths.map(w =>
sharp(input)
.resize(w, null, { withoutEnlargement: true })
.webp({ quality: 80 })
.toBuffer()
)
)
return sizes
}
// 生成 GIF 缩略图(转 PNG)
const toAnimated = async (input) => {
const meta = await sharp(input, { animated: true }).metadata()
if (meta.pages && meta.pages > 1) {
return sharp(input, { animated: true }).gif().toBuffer()
}
}
// WebP 批量转换
const convertToWebP = async (dir) => {
const files = fs.readdirSync(dir).filter(f => /\.(jpg|png)$/i.test(f))
for (const file of files) {
await sharp(path.join(dir, file))
.webp({ quality: 80 })
.toFile(path.join(dir, file.replace(/\.(jpg|png)$/i, '.webp')))
}
}
常见问题
| 问题 | 解决方案 |
|---|---|
| 安装报错 | npm install --sharp_binary_host 设置镜像,或手动安装 libvips |
| 图片方向错乱 | 加 .rotate() 自动根据 EXIF 方向旋转 |
| 内存占用高 | 用流式处理 pipe,不要一次性读入大文件 |
| 转换后文件变大 | 加 quality 参数(80-85 最佳) |
| GIF 不支持 | 转 PNG 序列,或用 gifencoder / gif-encoder-2 |
| SVG 转位图 | 先 convertSVGtoPDF 再处理,或用 librsvg |
| 想处理特定帧 | 用 sharp(input, { animated: true }) + .gif() |