需求起源是对象整理音效的时候,她收集的音效资源是有嵌套的子文件夹的,可是她想把全部的文件都提到一级目录,没有程序以前,她的操做是:css
若是子文件夹少,那工做量也还算不大,可是若是子文件夹嵌套较深,这工做量就上来了,并且都是重复的工做。因而她过来寻求个人帮助,我一看恰好本身最近不是在学习node吗,这恰好能够练习下node。vue
实现流程主体是一个递归,遍历文件夹,若是是文件就执行转移操做,不是就传入新的文件夹路径,继续遍历。node
具体实现分如下几步:git
由于使用平台为windows,而后又不能把界面作丑,因此技术选择为Electron + Node。虽然electron打包出来文件有 50M,可是软件带来的收益是远远大于须要付出的代价的。对于electron与vue的集成,以前写过文章 Electron+vue从零开始打造一个本地音乐播放器。electron与vue集成目前社区有两个方案:github
这里我弄了一个很简单的项目初始化模板,集成最新稳定版本的electron,以及最新的vue-cli4,另外还添加了normalize.css初始化样式。electron+vue相关项目的初始化能够直接使用这个模板,戳这里。web
获取文件路径,这里的处理方式,跟以前的翻译项目(Electron+Vue从零开始打造一个本地文件翻译器)同样,经过两种方式获取到须要的路径。vue-cli
这里不同的是须要对拖入的文件进行判断,必须拖入的是文件夹。windows
const originFiles = [...e.dataTransfer.files];
const isAllDir = originFiles.every(file =>
fs.statSync(file.path).isDirectory()
);
if (!isAllDir) {
ipcRenderer.send("confirmDialog");
return false;
}
复制代码
主进程markdown
ipcMain.on("confirmDialog", () => {
dialog.showMessageBox({
type: "info",
title: "确认",
message: "请确认选择的文件是否都是文件夹"
});
});
复制代码
获取路径下全部的文件dom
// 获取文件路径下的全部文件
async getAllFiles(path) {
try {
const res = await fsp.readdir(path);
return res;
} catch (error) {
console.log(error);
}
},
复制代码
转移操做这里使用的是fs模块的rename方法,须要判断是不是文件夹来决定是否须要递归操做。对于重名文件,会判断待转移文件与目标文件夹文件大小是否一致,若是不一致会从新命名。从新命名须要生成一个文件id附带文件名称在后面,保证文件不会重名。具体代码以下:
判断是不是文件夹
// 判断是不是文件夹
async isDir(path) {
try {
const res = await fsp.stat(path);
if (res.isDirectory()) {
return true;
} else {
return false;
}
} catch (error) {
console.log(error);
}
},
复制代码
生成文件id
// 生成文件id
uuid(len, radix) {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
let uuid = [],
i;
radix = radix || chars.length;
if (len) {
// Compact form
for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
} else {
// rfc4122, version 4 form
var r;
// rfc4122 requires these characters
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
// Fill in random data. At i==19 set the high bits of clock sequence as
// per rfc4122, sec. 4.1.5
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | (Math.random() * 16);
uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
}
}
}
return uuid.join('');
},
复制代码
转移操做
/*
dirPath: 原文件夹
targetPath: 新的目标文件夹
*/
async moveFiles(dirPath, targetDirPath) {
const files = await this.getAllFiles(dirPath);
files.forEach(async (file, index) => {
const filePath = path.resolve(dirPath, file);
const targetFilePath = path.resolve(targetDirPath, file);
let isDir = await this.isDir(filePath);
//不是目录就执行复制,否者递归
if (!isDir) {
// 检查移动目标文件夹是否有重名文件
console.log('遍历了全部的文件');
console.log({ filePath, targetFilePath });
if (fs.existsSync(targetFilePath)) {
console.log(`目标文件 ${file} 存在于目标文件夹 ${targetDirPath} 中`);
console.log({ filePath, targetFilePath });
const targetFiles = await this.getAllFiles(targetDirPath);
const fileIndex = targetFiles.indexOf(file);
console.log(`同名文件 ${file} 的下标为 ${fileIndex}`);
// 获取两边文件的信息,进行对比
const targetFileInfo = await fsp.stat(targetFilePath);
const originFileInfo = await fsp.stat(filePath);
console.log(`原文件与目标文件夹文件的大小分别为 ${originFileInfo.size} ${targetFileInfo.size}`);
// 若是有重名文件,判断文件大小是否一致
if (fileIndex >= 0 && originFileInfo.size !== targetFileInfo.size) {
// 获取原文件的名称以及后缀格式
const fileExt = path.extname(filePath);
const fileName = path.basename(filePath, fileExt);
// 生成新的文件名称
const newFileName = `${fileName}-${this.uuid(6, 16)}`;
const newPath = path.resolve(dirPath, `${newFileName}${fileExt}`);
const newTargetFilePath = path.resolve(targetDirPath, `${newFileName}${fileExt}`);
// 重命名
await fsp.rename(filePath, newPath);
// 移动至新目标文件夹
await fsp.rename(newPath, newTargetFilePath);
} else {
console.log('目标文件同名可是文件内容不同');
console.log({ filePath, targetFilePath });
// 不是同名文件就直接复制移动
await fsp.rename(filePath, targetFilePath);
}
} else {
console.log('目标文件不存在于目标文件夹中');
await fsp.rename(filePath, targetFilePath);
}
} else {
// 是目录,执行递归操做
await this.moveFiles(filePath, targetDirPath);
}
let timer = setTimeout(async () => {
await this.removeDir(dirPath);
this.loading = false;
clearTimeout(timer);
}, 1000);
});
},
复制代码
因为执行完复制移动方法后原文件夹还存在,因此须要删除。删除操做也是须要查询是不是文件夹,若是是文件就执行fs.unlinkSync方法,是目录就进行递归,最后删除完,保证了仅剩下目录,而后经过fs.rmdirSync,执行删除目录的操做。具体代码以下
// 删除文件
removeDir(url) {
if (fs.existsSync(url)) {
const files = fs.readdirSync(url);
files.forEach((file, index) => {
const curPath = path.join(url, file);
if (fs.statSync(curPath).isDirectory()) {
console.log(`检测到目录 ${curPath}`);
this.removeDir(curPath);
} else {
fs.unlinkSync(curPath);
console.log(`已删除文件 ${curPath}`);
}
});
// 删除文件夹
fs.rmdirSync(url);
} else {
console.log('已删除文件');
}
},
复制代码
最近在对象面前装逼太多了,在对象眼里我都是发光的存在,她就跟她的同窗讲程序猿有多神奇(虽然在我们眼里是小事),而后她的同窗们都纷纷说要找程序猿男朋友,我内心暗喜,我终于为我们程序猿争光了!!!源码在这 戳这里