函数式编程实例:node.js图片处理工具sharp使用指南

前言

上一篇文章简单介绍了一下函数式编程,碰巧最近基于 Node.jssharp 模块做了一个图片压缩工具,想写一篇文章记录一下。

于是就用函数式编程思想对这个工具就行了重写,本文会着重介绍 sharp 模块的应用,所以和我自己做的工具有关系但是不大,为了方便理解函数式编程我拆解掉了一些复杂的业务功能。

本文旨在介绍 sharp 模块使用的同时,向大家展示函数式编程的应用,因笔者能力有限,文中难免有错误指出,欢迎大家评论区留言勘误。

sharp 介绍

sharp.js 是一个流行的 Node.js 图像处理库,它提供了快速、高质量的图像处理功能。sharp.js 可以用于调整图像大小、裁剪、旋转、锐化、模糊、添加水印等各种图像处理操作。它支持多种图像格式,并提供了灵活的选项和链式调用的方式来进行图像处理。

sharp 是基于 libvips 库实现的。libvips 是一个开源的图像处理库,它具有高性能和低内存消耗的特点。sharp 利用 libvips 提供的强大功能和优化算法来实现快速且高质量的图像处理操作。

sharp 利用 libvips 提供的功能,提供了一种简单而高效的 JavaScript 接口,使开发者能够在 Node.js 环境中轻松进行图像处理。通过与 libvips 的紧密集成,sharp 可以充分利用 libvips 的性能和优化,从而实现快速的图像处理操作。

因此,sharp 的强大和高性能主要来自于底层的 libvips 库,而 sharp 则提供了一个友好的 JavaScript 接口,使开发者能够便捷地使用 libvips 进行图像处理。

sharp官网:https://sharp.pixelplumbing.com/

Github项目:https://github.com/lovell/sharp

sharp 使用

sharp 使用非常简单,它支持像 jQuery 那样的链式调用。它的使用非常简单,下面是一个简单实例,这个实例把一张图片大小调整成300*300,然后进行了裁切,并且把图片质量调整到10来减少图片size,最后保存到本地。

// 引入
const sharp = require('sharp')

// 使用

sharp('./images/测试图片.jpg')
    .resize(300, 300)
    .extract({left: 100, top: 100, width: 200, height: 200})
    .jpeg({ quality: 10 })
    .toFile('./output/demo.jpg');

使用起来很简单,接下来,我们来用函数式编程的思想对它进行重写。

实例讲解

这个实例实现了对图片的缩放、裁剪、格式转换、定制图片优化等功能。使用函数式编程思想把一个个小功能定义成对应的纯函数。最后在处理图片的时候进行组合,实现最终的功能。为了方便功能的最后组合,封装了一个 pipe 管道函数,让所有的方法如同在流水线上一下,按一定的顺序进行执行。

单独的功能函数都不复杂,如果阅读工程中无法串起来,可以直接看下面的调用。

压缩图片

/**
 * @description 缩放、压缩图片
 * @param {Number} width 图片宽度
 * @param {Number} height 图片高度
 */

function resizeImage(width, height) {
    return async function (imageBuffer) {
        return await sharp(await imageBuffer).resize(width, height).toBuffer();
    }
}

裁剪图片

/**
 * @description 提取、裁切图片
 * @param {Number} x x轴
 * @param {Number} y y轴
 * @param {Number} w 截取宽度
 * @param {number} h 截取高度
 */
function cropImage(x, y, w, h) {
    return async function (imageBuffer) {
        return sharp(await imageBuffer).extract({ left: x, top: y, width: w, height: h}).toBuffer();
    }
}

JPG图片处理

JPG图片处理的可选参数如下:

参数 类型 默认值 描述
quality number 80 质量,整数 1-100
progressive boolean false 使用逐行(隔行)扫描
chromaSubsampling string “‘4:2:0′” 设置为“4:4:4”以防止色度子采样,否则默认为“4:2:0”色度子采样
optimiseCoding boolean true 优化哈夫曼编码表
optimizeCoding boolean true optimiseCoding 的替代拼写
mozjpeg boolean false 使用 mozjpeg 默认值,相当于{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }
trellisQuantization boolean false 应用网格量化
overshootDeringing boolean false 应用过冲去振铃
optimiseScans boolean false 优化逐行扫描,强制逐行扫描
optimizeScans boolean false optimiseScans 的替代拼写
quantisationTable number 0 要使用的量化表,整数 0-8
quantizationTable number 0 quantizationTable 的替代拼写
force boolean true 强制 JPEG 输出,否则尝试使用输入格式
/**
 * @description 对jpg图片进行处理
 * @param {Object} options 
 */

function convertToJPG(options) {
    return async function(imageBuffer) {
        return sharp(await imageBuffer).jpeg(options).toBuffer();
    }
}

PNG图片处理

PNG图片处理的可选参数如下:

参数 类型 默认值 描述
progressive boolean false 使用逐行(隔行)扫描
compressionLevel number 6 zlib 压缩级别,0(最快、最大)到 9(最慢、最小)
adaptiveFiltering boolean false 使用自适应行过滤
palette boolean false 量化为具有 alpha 透明度支持的基于调色板的图像
quality number 100 使用达到给定质量所需的最少颜色数,设置palette为true
effort number 7 CPU 工作量介于 1(最快)和 10(最慢)之间,设置palette为true
colours number 256 调色板条目的最大数量,设置palette为true
colors number 256 options.colours,的替代拼写palette为true
dither number 1.0 Floyd-Steinberg 误差扩散水平,设置palette为true
force boolean true 强制 PNG 输出,否则尝试使用输入格式
/**
 * @description 对png图片进行处理
 * @param {Object} options 
 */

function convertToPNG() {
    return async function(imageBuffer) {
        return sharp(await imageBuffer).png(options).toBuffer();
    }
}

转变图片格式

/**
 * @description 转变图片格式
 * @param {*} format 
 * @returns 
 */
function convertToFormat (format) {
    return async function (imageBuffer) {
        return sharp(await imageBuffer).toFormat(format).toBuffer();
    }
}

保存图片

/**
 * @description 保存图片到指定路径
 * @param {String} 图片保存路径
 */
function saveImage(outputPath) {
    return async function (imageBuffer) {
        return sharp(await imageBuffer).toFile(outputPath)
    }
}

管道(辅助)函数

管道函数是一个编程概念,用于将多个函数连接在一起,形成一个函数链。每个函数的输出作为下一个函数的输入,数据在这个函数链中顺序流动,类似于管道中的流体。

管道函数的主要作用是简化代码和增强可读性。通过将多个函数链接在一起,可以将复杂的操作拆分成更小、更可管理的部分。每个函数只需关注自己的输入和输出,而不需要关心整个处理流程。这样可以降低代码的复杂性和维护成本。

另外,管道函数还可以提高代码的可组合性和可重用性。每个函数都可以在不同的上下文中使用,只需根据需要重新组合函数链即可。这种组合性使得代码更加灵活,可以根据需求进行定制化的数据处理。

/**
 * @description 辅助函数
 * @param {Function} 管道函数
 */
function pipe(...fns) {
    return function (x) {
        return fns.reduce((v, fn) => fn(v), x);
    }
}

功能组合

/**
 * @description 组合功能实现对图片的处理
 * @param {*} inputPath 图片输入路径
 * @param {*} outputPath 图片输出路径
 * @param {*} options 其他图片配置
 * @returns 
 */
function processImage (inputPath, outputPath, options) {
    const { width, height, format, crop } = options;

    // 压缩和裁剪图片
    const resizeAndCrop = pipe(
        resizeImage(width, height),
        crop ? cropImage(crop.x, crop.y, crop.w, crop.h) : (imageBuffer) => imageBuffer
    )

    // 保存图片
    const saveToFile = saveImage(outputPath);

    // 组合调用
    return pipe(
        () => sharp(inputPath).toBuffer(),
        resizeAndCrop,
        convertToFormat(format),
        convertToPNG({ quality: 10}),
        saveToFile
    )();
}

实例应用

const inputPath = './images/测试图片.jpg';
const outputPath = './output/demo.png';
const options = {
    width: 500,
    height: 400,
    format: 'png',
    crop: {
        x: 100,
        y: 100,
        w: 300,
        h: 300
    }
}

processImage(inputPath, outputPath, options).then(() => {
    console.log('图片处理并保存成功');
}).catch((err) => {
    console.log('图片处理过程中发生错误:', err.message);
});

总结

本文简要介绍了 sharp 模块,它是一个基于 Node.js 的高性能图像处理库。通过与底层的 libvips 库紧密集成,sharp 可以实现快速且高质量的图像处理操作。同时,本文还向大家展示了函数式编程思想在实际项目中的应用。通过将复杂的业务逻辑拆解成小的纯函数,并利用管道函数进行组合,可以实现代码的简化、可读性的增强,同时还提高了代码的可组合性和可重用性。希望本文能够帮助大家更好地理解 sharp 模块的使用,并对函数式编程的应用有进一步的认识。

原文链接:https://juejin.cn/post/7329341975208034342 作者:东方红杉

(0)
上一篇 2024年1月29日 下午5:13
下一篇 2024年1月30日 上午10:05

相关推荐

发表回复

登录后才能评论