NodeJs 批量图片瘦身,重设尺寸和图片质量并保存到指定目录
源代码(image-resize.js)
/**
* NodeJs 批量图片瘦身,重设尺寸和图片质量并保存到指定目录
* 功能:批量图片瘦身,可指定宽度和图片质量并输出保存到指定目录
* 使用:node image-resize.js
* 扩展包:npm install images
*/
// 安装并引用 images 模块处理图片瘦身
const NmImages = require('images')
// 引用 fs 文件系统模块
const NmFs = require('fs')
// 引用 path 路径处理模块
const NmPath = require('path')
// 配置信息
const config = {
// 图片格式后缀
image_exts: ['jpg', 'png', 'gif', 'jpeg', 'webp', 'tiff'],
// 图片大小与压缩质量配置
quantity_config: {
// 是否启用
enable: true,
// 注意:顺序必须从大到小
values: [
// 50 MB 以上 30
{
size: 52428800,
quantity: 30
},
// 30 MB 以上 50
{
size: 31457280,
quantity: 50
},
// 20 MB 以上 60
{
size: 20971520,
quantity: 60
},
// 15 MB 以上 70
{
size: 15728640,
quantity: 70
},
// 10 MB 以上 80
{
size: 10485760,
quantity: 80
},
// 默认 80
{
size: 0,
quantity: 80
},
]
}
}
/**
* 批量生成缩略图到指定目录(目标目录的层级结构和来源目录保持一致)
* @param {String} fromDir 来源目录
* @param {String} toDir 目标目录
* @param {Boolean} isDebug 是否调试模式。调试模式只会在控制台输出信息,不会真正操作文件
* @param {Boolean} isSkipExists 是否跳过已存在的目标文件
* @param {Number} width 图片宽度。最小 1080, 最大 3048
* @param {Number} quality 图像质量
*/
async function resizeImages(fromDir, toDir, isDebug = true, isSkipExists = true, width = 0, quality = 80) {
if (!NmFs.existsSync(fromDir)) {
console.log('path not exists: ', fromDir);
return;
}
// 自动创建目标路径
if (!isDebug && !NmFs.existsSync(toDir)) {
NmFs.mkdirSync(toDir, {
recursive: true
});
}
// 自动补齐路径符
const SEP = NmPath.sep;
if (!fromDir.endsWith(SEP)) {
fromDir += SEP;
}
if (!toDir.endsWith(SEP)) {
toDir += SEP;
}
// 打开目录
const dir = await NmFs.promises.opendir(fromDir);
// 声明变量,优化内存
let ext = '',
newPath = '',
currentPath = '',
fromFileSize = '0 B',
toFileSize = '0 B';
for await (const dirent of dir) {
// 当前路径
currentPath = fromDir + dirent.name;
newPath = toDir + dirent.name;
// 处理目录
if (dirent.isDirectory()) {
// 如果当前路径是目录,则进入递归模式
resizeImages(currentPath + SEP, newPath + SEP, isDebug, isSkipExists, width, quality);
continue;
}
// 处理文件
ext = NmPath.extname(dirent.name); // .jpg
if (!ext) {
continue;
}
ext = ext.substring(1).toLowerCase();
// 过滤非图片格式的文件
if (!config.image_exts.includes(ext)) {
continue;
}
// 自动图片质量
fromFileSize = NmFs.statSync(currentPath).size;
if (config.quantity_config.enable) {
for (let kv of config.quantity_config.values) {
if (fromFileSize > kv.size) {
quantity = kv.quantity;
break;
}
}
}
fromFileSize = bytesFormatter(fromFileSize);
// 重设图片尺寸并保存
if (isDebug) {
console.log(`[缩略图] [${fromFileSize}/${toFileSize}/Q${quantity}]`, currentPath, '=>', newPath);
continue;
}
// 判断是否过滤已存在的目标文件
if (isSkipExists && NmFs.existsSync(newPath)) {
toFileSize = bytesFormatter(NmFs.statSync(newPath).size);
console.log(`[已存在] [${fromFileSize}/${toFileSize}/Q${quantity}]`, currentPath, `=>`, newPath);
continue;
}
// 照片瘦身
try {
if (width > 0) {
NmImages(currentPath).resize(width).save(newPath, {
quality: quality
});
} else {
NmImages(currentPath).save(newPath, {
quality: quality
});
}
toFileSize = bytesFormatter(NmFs.statSync(newPath).size);
console.log(`[缩略图] [${fromFileSize}/${toFileSize}/Q${quantity}]`, currentPath, '=>', newPath);
} catch (error) {
console.log(`[错误] [${fromFileSize}/${toFileSize}/Q${quantity}]`, currentPath, '=>', newPath);
}
}
}
//文件大小换算
function bytesFormatter(bytes = 0) {
if (bytes === 0) {
return '0 B';
}
let k = 1024,
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
let num = bytes / Math.pow(k, i);
// 优化:1023.999 KB 显示为 0.9xx MB
if (num > 1000) {
i += 1;
num = bytes / Math.pow(k, i);
}
return num.toPrecision(3) + ' ' + sizes[i];
}
// 执行批量照片瘦身功能
// 注意,会内存溢出,是image插件的问题,暂时没有解决办法,只能报错后重新运行,循环接力直到完成
// NmImages.setGCThreshold(1024 * 1024 * 1024); // 设置内存自动回收阈值为 1GB。会出错!
const FROM_PATH = 'F:\\Downloads\\Images';
const TO_PATH = FROM_PATH + ' Lite';
const IS_DEBUG = false;
const IS_SKIP_EXISTS = true;
resizeImages(FROM_PATH, TO_PATH, IS_DEBUG, IS_SKIP_EXISTS).catch(err => console.log(err))
执行
node image-resize.js