源代码(file-rename.js)
// 文件路径:file-rename.js
/**
* NodeJs 批量重命名文件,并保存到指定目录,支持递归目录
* 功能:批量重命名文件,并保存到指定目录,支持递归目录
* 使用:node file-rename.js
* 扩展包:无
*/
// 引用 fs 文件系统模块
const NmFs = require('fs');
// 引用 path 路径处理模块
const NmPath = require('path');
// 配置信息
const config = {
// 过滤字符串模式
filter_text: {
enable: false,
text: '',
},
// 前面删除模式
trim_start: {
enable: true,
// 删除指定的字符串
text: '',
// 删除 {length} 个字符
length: 10,
// 删除 {length} 必须包含的关键词
length_key: 'NO.',
},
// 后面删除模式
trim_end: {
enable: false,
// 删除指定的字符串
text: '',
// 删除 {length} 个字符
length: 0
},
// 后面追加模式
append: {
enable: false,
text: '-append',
},
// 前面追加件模式
prepend: {
enable: false,
text: 'prepend-',
},
// 中间替换或插入模式
splice: {
enable: false,
// 删除的开始位置
start: 0,
// 被删除的字符数量,数值 0 表示插入字符串
length: 0,
// 要替换或插入的字符串
text: '',
},
// 允许处理的文件扩展名
allow_exts: {
enable: false,
exts: ['jpg', 'png', 'gif', 'jpeg', 'webp', 'tiff'],
},
// 跳过处理的文件扩展名
skip_exts: {
enable: false,
exts: []
}
}
/**
* 更改文件名
* @param {String} basename 文件名,无扩展名
* @param {String} ext 文件扩展名
*/
function changeName(basename = '', ext = '', isDirMode = false) {
if (!isDirMode && ext) {
if ((config.allow_exts.enable && !config.allow_exts.exts.includes(ext))) {
return basename;
}
if (config.skip_exts.enable && config.skip_exts.exts.includes(ext)) {
return basename;
}
}
let nameLength = basename.length;
// 文件名前面移除字符
if (config.trim_start.enable) {
// 字符串匹配删除模式
if (config.trim_start.text.length > 0 && basename.indexOf(config.trim_start.text) > -1) {
// basename = basename.substring(config.trim_start.text.length);
basename = basename.replace(config.trim_start.text, '');
}
// 字符长度删除模式
if (config.trim_start.length > 0 && (nameLength > config.trim_start.length)) {
console.log(basename, config.trim_start.length_key, basename.indexOf(config.trim_start.length_key));
if ((config.trim_start.length_key.length == 0) || (config.trim_start.length_key.length > 0 && basename.indexOf(config.trim_start.length_key) > 0)) {
basename = basename.substring(config.trim_start.length);
}
}
}
// 文件名后面移除字符
if (config.trim_end.enable) {
// 字符串匹配删除模式
if (basename.indexOf(config.trim_end.text) > -1) {
// basename = basename.substring(0, nameLength - config.trim_end.text.length);
basename = basename.replace(config.trim_end.text, '');
}
// 字符长度删除模式
if (config.trim_end.length > 0 && nameLength > config.trim_end.length) {
basename = basename.substring(0, nameLength - config.trim_end.length);
}
}
// 过滤字符串模式
if (config.filter_text.enable && config.filter_text.text.length > 0) {
basename = basename.replace(new RegExp(config.filter_text.text, 'ig'), '');
}
// 中间替换或插入模式
if (config.splice.enable) {
let a = basename.substring(0, config.splice.start);
let b = basename.substring(config.splice.start + config.splice.length);
basename = a + config.splice.text + b;
}
// 文件名前面插入字符
if (config.prepend.enable) {
basename = config.prepend.text + basename;
}
// 文件名后面插入字符
if (config.append.enable) {
basename = basename + config.append.text;
}
return basename;
}
/**
* 批量更改文件名到指定目录
* @param {String} fromDir 来源目录
* @param {String} toDir 目标目录
* @param {Boolean} isDebug 是否调试模式。调试模式不会执行重命名操作,只会输出控制台日志
* @param {Boolean} isSkipExists 是否跳过已存在的目标文件
* @param {Boolean} isDirMode 是否重命名对象为目录,否则为文件
* @param {Boolean} isRecursion 是否递归遍历子目录
*/
async function renameFiles(fromDir, toDir, isDebug = true, isSkipExists = true, isDirMode = false, isRecursion = true) {
if (!NmFs.existsSync(fromDir)) {
console.log('path not exists: ', fromDir);
return;
}
// 自动创建目标路径
if (!isDebug && !NmFs.existsSync(toDir)) {
NmFs.mkdirSync(toDir, {
recursive: true
});
}
let sep = NmPath.sep;
// 自动补齐路径符
if (!fromDir.endsWith(sep)) {
fromDir += sep;
}
if (!toDir.endsWith(sep)) {
toDir += sep;
}
// 打开目录
const dir = await NmFs.promises.opendir(fromDir);
// 声明变量,优化内存
let basename = '',
ext = '',
newPath = '',
currentPath = '';
for await (const dirent of dir) {
// 当前路径
currentPath = fromDir + dirent.name;
newPath = toDir + dirent.name;
// 处理目录
if (dirent.isDirectory()) {
// 如果当前路径是目录,则进入递归模式
if (isDirMode) {
ext = '';
basename = NmPath.basename(dirent.name, ext); // 无扩展名
basename = changeName(basename, ext);
newPath = toDir + basename;
// 判断是否过滤已存在的目标文件
if (isSkipExists && NmFs.existsSync(newPath)) {
console.log('[已存在]', currentPath, '=>', newPath);
continue;
}
if (!isDebug) {
NmFs.renameSync(currentPath, newPath);
}
console.log('[重命名]', currentPath, '=>', newPath);
}
if (isRecursion) {
renameFiles(currentPath + sep, newPath + sep, isDebug, isSkipExists, isDirMode, isRecursion);
}
continue;
}
if (isDirMode) {
continue;
}
// 文件名
ext = NmPath.extname(dirent.name); // .jpg
basename = NmPath.basename(dirent.name, ext); // 无扩展名
ext = ext.substring(1).toLowerCase();
basename = changeName(basename, ext);
newPath = toDir + basename + '.' + ext;
// 判断是否过滤已存在的目标文件
if (isSkipExists && NmFs.existsSync(newPath)) {
console.log('[已存在]', currentPath, '=>', newPath);
continue;
}
if (!isDebug) {
NmFs.renameSync(currentPath, newPath);
}
console.log('[重命名]', currentPath, '=>', newPath);
}
}
// 执行批量文件重命名功能
const FROM_DIR = 'F:\Downloads\Images';
const TO_DIR = FROM_DIR;
const IS_DEBUG = false;
const IS_SKIP_EXISTS = true;
const IS_DIR_MODE = true;
const IS_RECURSION = false;
renameFiles(FROM_DIR, TO_DIR, IS_DEBUG, IS_SKIP_EXISTS, IS_DIR_MODE, IS_RECURSION).catch(err => console.log(err))
运行文件
node file-rename.js