需求起源是對象整理音效的時候,她收集的音效資源是有嵌套的子文件夾的,可是她想把全部的文件都提到一級目錄,沒有程序以前,她的操做是: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('已刪除文件');
}
},
複製代碼
最近在對象面前裝逼太多了,在對象眼裏我都是發光的存在,她就跟她的同窗講程序猿有多神奇(雖然在我們眼裏是小事),而後她的同窗們都紛紛說要找程序猿男朋友,我內心暗喜,我終於爲我們程序猿爭光了!!!源碼在這 戳這裏