上一篇文章讲了如何使用 pngquant
这次我们来试试使用 node.js 封装一下 pngquant,实现一个自动化批量压缩图片的脚本
准备
有关如何使用 node.js 开发一个命令行工具,可以参考我之前写的一篇文章
站内链接
使用node.js开发命令行工具
使用node.js开发命令行工具教程
首先我们先初始化一个 nodejs 项目
1 2 3
| mkdir node-pngquant cd node-pngquant npm init -y
|
接着创建一个 bin 目录,并在该目录下新建一个 js 脚本
1 2 3
| mkdir bin cd bin touch pngquant.js
|
然后配置 package.json 文件,文件内容类似于这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| { "name": "pngquant", "version": "1.0.0", "main": "index.js", "type": "module", "bin": { "node-pngquant": "bin/pngquant.js" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "description": "" }
|
接着回到项目根目录,安装 commander 依赖
1 2
| cd .. # 返回根目录 npm install commander
|
js 代码编写
然后我们开始编写 js 脚本
首先导入需要的包
1 2 3 4 5 6 7
| #!/usr/bin/env node
import { program } from "commander"; import { exec } from "node:child_process"; import path from "node:path"; import fs from "node:fs"; import { fileURLToPath } from "node:url";
|
接着使用 commander 构建版本命令和一些参数
有关 commander 的使用,可以参考官方文档
引用站外链接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const packageJsonPath = new URL("../package.json", import.meta.url); const packageJson = JSON.parse( fs.readFileSync(fileURLToPath(packageJsonPath), "utf-8") );
program .version(packageJson.version, "-v, --version", "输出当前工具版本号") .description( `一个使用node.js封装的pngquant命令行工具,用于批量压缩处理图片,开发者为✨星凪✨ 使用格式为 node-pngquant [options] [dir] [output] [quality] 例如 node-pngquant -d ./imgs -o ./output -q 85 可以简写为 node-pngquant ./imgs ./output 85 ` );
program.helpOption("-h, --help", "输出帮助信息");
program.option("-d, --dir <dir>", "指定要压缩的图片目录");
program.option("-q, --quality <quality>", "指定压缩质量,范围0-100,默认85");
program.option("-o, --output <output>", "指定输出目录");
|
接着我们需要定义一个默认的压缩参数
process.cwd()用于获取当前的工作目录
1 2 3 4 5 6
| const params = { dir: path.resolve(process.cwd()), quality: 85, output: path.resolve(process.cwd()), };
|
接下载在写一个工具函数,用于判断文件是否是一个目录
使用 Promise 的好处在于避免了回调函数的使用,可以让代码更加简洁
1 2 3 4 5 6 7 8 9 10 11
| const checkIsDirectory = (dir) => { return new Promise((resolve, reject) => { fs.stat(dir, (err, stats) => { if (err) { reject(err); } else { resolve(stats.isDirectory()); } }); }); };
|
接着我们开始编写压缩单个文件的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const compressPng = (fileName, params) => { return new Promise((resolve, reject) => { exec( `pngquant ${path.join(params.dir, fileName)} --quality=${ params.quality } --output=${path.join(params.output, "compress", fileName)}`, (err, _stdout, _stderr) => { if (err) { reject(err); } else { console.log(fileName); resolve(); } } ); }); };
|
最后一步,获取用户输入的参数并执行命令
options 为输入的参数,例如 node-pngquant -d ./imgs
,会获取到 ./imgs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| program.action(async (options) => { try { if (options.dir) { params.dir = path.resolve(options.dir); } if (options.quality) { params.quality = options.quality; } if (options.output) { params.output = path.resolve(options.output); } if (Object.keys(options).length === 0) { if (program.args[0]) { params.dir = path.resolve(program.args[0]); } if (program.args[1]) { params.output = path.resolve(program.args[1]); } if (program.args[2]) { params.quality = program.args[2]; } } const isDirectory = await checkIsDirectory(params.dir); if (!isDirectory) { console.error("输入路径不是一个目录"); return; } const files = fs.readdirSync(params.dir, { withFileTypes: true, encoding: "utf-8", }); if (!fs.existsSync(path.join(params.output, "compress"))) { fs.mkdirSync(path.join(params.output, "compress")); } for (const file of files) { if (!file.isFile() || !file.name.endsWith(".png")) { continue; } await compressPng(file.name, params); } } catch (error) { console.error("发生了未知错误\n", error); } });
program.parse(process.argv);
|
效果预览
输入
1
| node-pngquant -d ./imgs -o ./ -q 85
|
输出信息
1 2 3 4
| a-xingguang2x.png sticker-fs8.png sticker.png test.png
|
输入命令后
总代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
| #!/usr/bin/env node
import { program } from "commander"; import { exec } from "node:child_process"; import path from "node:path"; import fs from "node:fs"; import { fileURLToPath } from "node:url";
const packageJsonPath = new URL("../package.json", import.meta.url); const packageJson = JSON.parse( fs.readFileSync(fileURLToPath(packageJsonPath), "utf-8") );
program .version(packageJson.version, "-v, --version", "输出当前工具版本号") .description( `一个使用node.js封装的pngquant命令行工具,用于批量压缩处理图片,开发者为✨星凪✨ 使用格式为 node-pngquant [options] [dir] [output] [quality] 例如 node-pngquant -d ./imgs -o ./output -q 85 可以简写为 node-pngquant ./imgs ./output 85 ` );
program.helpOption("-h, --help", "输出帮助信息");
program.option("-d, --dir <dir>", "指定要压缩的图片目录");
program.option("-q, --quality <quality>", "指定压缩质量,范围0-100,默认85");
program.option("-o, --output <output>", "指定输出目录");
const params = { dir: path.resolve(process.cwd()), quality: 85, output: path.resolve(process.cwd()), };
const checkIsDirectory = (dir) => { return new Promise((resolve, reject) => { fs.stat(dir, (err, stats) => { if (err) { reject(err); } else { resolve(stats.isDirectory()); } }); }); };
const compressPng = (fileName, params) => { return new Promise((resolve, reject) => { exec( `pngquant ${path.join(params.dir, fileName)} --quality=${ params.quality } --output=${path.join(params.output, "compress", fileName)}`, (err, _stdout, _stderr) => { if (err) { reject(err); } else { console.log(fileName); resolve(); } } ); }); };
program.action(async (options) => { try { if (options.dir) { params.dir = path.resolve(options.dir); } if (options.quality) { params.quality = options.quality; } if (options.output) { params.output = path.resolve(options.output); } if (Object.keys(options).length === 0) { if (program.args[0]) { params.dir = path.resolve(program.args[0]); } if (program.args[1]) { params.output = path.resolve(program.args[1]); } if (program.args[2]) { params.quality = program.args[2]; } } const isDirectory = await checkIsDirectory(params.dir); if (!isDirectory) { console.error("输入路径不是一个目录"); return; } const files = fs.readdirSync(params.dir, { withFileTypes: true, encoding: "utf-8", }); if (!fs.existsSync(path.join(params.output, "compress"))) { fs.mkdirSync(path.join(params.output, "compress")); } for (const file of files) { if (!file.isFile() || !file.name.endsWith(".png")) { continue; } await compressPng(file.name, params); } } catch (error) { console.error("发生了未知错误\n", error); } });
program.parse(process.argv);
|
总结
我将这个包发布到了 npm,有兴趣的话可以使用 npm install node-pngquant -g
全局安装并使用。
不想使用了可以使用 npm uninstall node-pngquant -g
来删除
其实很多命令行工具都可以使用 node.js 进行封装并使用,达成更好的效果