高性能Node.js图像处理库sharp

高性能Node.js图像处理库sharp

2020年11月18日 阅读:26 字数:736 阅读时长:2 分钟

这个高速 Node.js 模块的典型用例是将常见格式的大图像转换为较小的、对网络友好的 JPEG、PNG、AVIF 和不同尺寸的 WebP 图像

这个高速 Node.js 模块的典型用例是将常见格式(支持读取 JPEG、PNG、WebP、AVIF、TIFF、GIF 和 SVG 图像)的大图像转换为较小的、对网络友好的 JPEG、PNG、AVIF 和不同尺寸的 WebP 图像,输出图像可以是 JPEG、PNG、WebP、AVIF 和 TIFF 格式以及未压缩的原始像素数据。

除了图像调整大小外,还可以进行旋转、提取、合成和伽马校正等操作。

由于使用libvips , 调整图像大小通常比使用最快的 ImageMagick 和 GraphicsMagick 设置快 4 到 5 倍。一次只将一小块未压缩的图像数据保存在内存中并进行处理,充分利用了多个 CPU 内核和 L1/L2/L3 缓存。由于libuv,一切都保持非阻塞,没有产生子进程并且支持 Promises/async/await。

文档:https://sharp.pixelplumbing.com/

如下案例展示了获取图片元数据和图片缩放、裁剪以及格式转换的功能

const path = require('path');
const fs = require('fs-extra');
const sharp = require('sharp');

module.exports = {
  /**
   * 获取图片元数据
   * @param {String} src 图片地址
   * @see https://sharp.pixelplumbing.com/api-input
   * @returns {Object}
   */
  async getMetadata(src) {
    // 源文件不存在
    const isExist = await fs.pathExists(src)
    if (!isExist ) {
      return {};
    }
    const metadata = await sharp(src).metadata();
    return metadata || {};
  }

  /**
   * 调整图像大小
   * @param {String} src 源图片地址
   * @param {String} dest 目标图片地址
   * @param {Object} resizeOpts 裁剪图片参数 { width, height, fit, position, background }
   * @summary fit: {0:'cover', 1:'contain', 2:'fill', 3: 'inside', 4: 'outside'}
   *          cover:裁剪以覆盖两个提供的尺寸(默认值)。
   *          contain:嵌入两个提供的尺寸。
   *          fill:忽略输入的纵横比并拉伸到两个提供的尺寸。
   *          inside:保留纵横比,将图像调整为尽可能大,同时确保其尺寸小于或等于指定的尺寸。
   *          outside:保留纵横比,将图像调整为尽可能小,同时确保其尺寸大于或等于指定的尺寸。
   * @summary {String} position: top,right top,right,right bottom,bottom,left bottom,left,left top
   * @summary background: 背景颜色 { r, g, b, alpha }
   * @param {Object} outputOpts 目标图片参数 { quality: jpg/webp的压缩质量, lossless: webp无损压缩, compressionLevel: png的zlib压实级别 }
   * @see https://sharp.pixelplumbing.com/api-output
   */
  async resizeAndCrop(options, outputOpts) {
    const { src, dest, destAbsolutePath, width, height, fit, position, background } = options;

    const fileSrc = path.join(think.RESOURCE_PATH, src);
    // 目标地址或源文件不存在
    const isExist = await fs.pathExists(fileSrc)
    if (!dest || !isExist) {
      return '';
    }

    let result;
    const extname = path.extname(dest); // 目标文件后缀

    // 先创建目标目录结构,才能输出图片
    try {
      await fs.ensureDir(path.dirname(destAbsolutePath));
    } catch (err) {
      console.error(err);
      return '';
    }

    // 先调整图像大小
    const fitEnum = ['cover', 'contain', 'fill', 'inside', 'outside'];
    const hasBackgroundEnum = ['.png', '.webp', '.tile'];
    const image = await sharp(fileSrc)
      .resize(width, height, {
        fit: fitEnum[fit] || 'cover',
        position,
        background: background || (
          hasBackgroundEnum.includes(extname)
            ? { r: 255, g: 255, b: 255, alpha: 0 }
            : { r: 255, g: 255, b: 255, alpha: 1 }
        )
      });

    // 再根据输出格式生成文件
    switch (extname) {
      case '.png':
        result = await image.png(outputOpts)
          .toFile(destAbsolutePath)
          .catch(error => console.error(error, fileSrc));
        break;
      case '.webp':
        result = await image.webp(outputOpts)
          .toFile(destAbsolutePath)
          .catch(error => console.error(error, fileSrc));
        break;
      default:
        const jpgOptions = {
          ...outputOpts,
          mozjpeg: true
        };
        result = await image.jpeg(jpgOptions)
          .toFile(destAbsolutePath)
          .catch(error => console.error(error, fileSrc));
    }

    return result ? dest : '';
  }
};

推荐阅读

恰饭区

评论区 0

0/500

还没有评论,快来抢第一吧