说在前面
最近裸辞出去玩了
两个多月
,旅行的过程中少不了拍照,祖国的大好河山太美了,一趟旅行下来产出了1w
多张图片,所以回来后总要整理一下图片,我这边想要的是根据拍摄时间来对图片进行分组,所以回到家后我就写了这样一个脚本来处理图片。
工具实现
1、模块导入
- inquirer
通过require(“@jyeontu/j - inquirer”)导入,用于创建命令行交互,让用户选择需要分组的目录。
- fs
使用require(“fs”)导入,用于进行文件系统的操作,如读取目录内容(readdirSync)、获取文件状态(statSync)、创建文件夹(mkdirSync)以及复制文件(copyFileSync)等操作。
path模块:通过require(“path”)导入,用于处理文件路径,如拼接路径(join)等操作。
- exif
使用require(“exif”).ExifImage导入,用于读取图片的 Exif 数据,以便获取图片的拍摄时间。
2、获取图片拍摄时间
function getPictureTakingTime(imagePath) {
return new Promise((resolve) => {
try {
new ExifImage({ image: imagePath }, function (error, exifData) {
if (error) {
resolve(null);
} else {
if (exifData && exifData.exif) {
const takingTime = exifData.exif.DateTimeOriginal;
resolve(takingTime);
} else {
resolve(null);
}
}
});
} catch (error) {
resolve(null);
}
});
}
- 函数接受一个图片路径imagePath作为参数,并返回一个Promise。
- 在函数内部,通过new ExifImage来尝试读取图片的 Exif 数据。如果读取过程中出现错误(error)或者图片没有 Exif 数据(exifData && exifData.exif不满足),则Promise返回null;如果成功读取到 Exif 数据并且包含DateTimeOriginal属性(表示拍摄时间),则将该拍摄时间作为Promise的结果返回。
3、通过交互获取需要分组的目录
const baseDir = process.cwd();
const options = [
{
type: "folder",
message: "请选择需要分组的目录:",
name: "directory",
default: "",
dirname: baseDir,
},
];
const answers = await new inquirer(options).prompt();
const dirPath = answers.directory;
- 首先获取当前工作目录(process.cwd())作为基础目录baseDir。
- 通过inquirer创建一个交互选项,提示用户选择需要分组的目录。用户选择的结果存储在answers.directory中,即dirPath变量。
4、遍历文件并分组
const files = fs.readdirSync(dirPath);
const map = {};
let sum = 0;
console.log(`正在获取图片信息,请稍等……`);
for (const file of files) {
const newPath = path.join(dirPath, file);
const info = fs.statSync(newPath);
if (!info.isFile()) {
continue;
}
sum++;
const m = (await getPictureTakingTime(newPath)) || dateFormat(info.mtime);
const mArr = m.split(" ");
mArr[0] = mArr[0].split(":").join("-");
const mtime = dateFormat(mArr.join(" "));
if (!map[mtime]) {
map[mtime] = [];
}
map[mtime].push(file);
}
- 使用fs.readdirSync(dirPath)读取目标目录下的所有文件和文件夹。
- 创建一个空对象map用于存储分组信息。
- 定义变量sum用于统计文件数量,初始值为 0。
- 遍历目标目录下的每个文件:
- 通过path.join(dirPath, file)构建文件的完整路径newPath。
- 使用fs.statSync(newPath)获取文件状态信息info,如果不是文件(!info.isFile())则跳过该文件。
- 对于每个文件,sum自增 1,并尝试获取图片拍摄时间。如果getPictureTakingTime(newPath)返回null(即没有拍摄时间 Exif 数据),则使用dateFormat(info.mtime)对文件修改时间进行格式化;否则使用拍摄时间。
- 对获取到的时间进行处理,将时间字符串中的:替换为-,然后再使用dateFormat进行格式化,得到最终的日期格式mtime。
- 如果map中不存在以mtime为键的属性,则创建一个空数组作为值;然后将文件名称添加到对应的日期数组中。
5、创建分组目录并移动文件
for (const key in map) {
const keyPath = path.join(dirPath, key);
if (!fs.existsSync(keyPath)) {
fs.mkdirSync(keyPath);
}
for (const p of map[key]) {
const np = path.join(keyPath, p);
if (fs.existsSync(np)) {
continue;
}
fs.copyFileSync(path.join(dirPath, p), path.join(keyPath, p));
}
}
- 遍历map中的每个日期键key:
- 通过path.join(dirPath, key)构建分组文件夹的路径keyPath。
- 如果该文件夹不存在(!fs.existsSync(keyPath)),则使用fs.mkdirSync(keyPath)创建文件夹。
- 遍历每个日期对应的文件数组中的文件p:
- 通过path.join(keyPath, p)构建目标文件路径np。
- 如果目标文件已经存在(fs.existsSync(np)),则跳过;否则使用fs.copyFileSync将文件从源路径(path.join(dirPath, p))复制到目标路径。
使用
该工具已经发布到 npm 上,可以直接通过命令npm i -g jyeontu
进行安装,安装完后在控制台中输入jyeontu file
即可进行操作。选择图片分组即可:
源码
Gitee
该工具的源码也已经开源,有兴趣的同学可以到Gitee上查看:https://gitee.com/zheng_yongtao/node-scripting-tool/tree/master/src/jyeontu
欢迎star~
公众号
关注公众号『前端也能这么有趣
』,获取更多有趣内容。
说在后面
🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『
前端也能这么有趣
』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。