• 微信:WANCOME
  • 扫码加微信,提供专业咨询
  • 服务热线
  • 13215191218
    13027920428

  • 微信扫码访问本页
desc-2
环企首页

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()