Electron+Vue+Node 實現展平文件夾小工具

GIF.gif

需求起源是對象整理音效的時候,她收集的音效資源是有嵌套的子文件夾的,可是她想把全部的文件都提到一級目錄,沒有程序以前,她的操做是:css

  1. 複製子文件夾中的文件到一級目錄
  2. 刪除子文件夾

若是子文件夾少,那工做量也還算不大,可是若是子文件夾嵌套較深,這工做量就上來了,並且都是重複的工做。因而她過來尋求個人幫助,我一看恰好本身最近不是在學習node嗎,這恰好能夠練習下node。vue

實現流程

1.png

實現流程主體是一個遞歸,遍歷文件夾,若是是文件就執行轉移操做,不是就傳入新的文件夾路徑,繼續遍歷。node

具體實現

具體實現分如下幾步:git

  1. 項目初始化
  2. 獲取文件路徑,以及路徑下全部文件
  3. 轉移操做
  4. 刪除操做

項目初始化

由於使用平臺爲windows,而後又不能把界面作醜,因此技術選擇爲Electron + Node。雖然electron打包出來文件有 50M,可是軟件帶來的收益是遠遠大於須要付出的代價的。對於electron與vue的集成,以前寫過文章 Electron+vue從零開始打造一個本地音樂播放器。electron與vue集成目前社區有兩個方案:github

  1. SimulatedGREG/electron-vue,這個比較老,比較臃腫,不過若是是老項目的遷移的話,能夠考慮使用。
  2. nklayman/vue-cli-plugin-electron-builder,這個支持最新穩定版本的electron以及最新的vue腳手架,代碼裏主進程相關代碼集成在background.js中,其餘代碼組織跟寫vue項目沒有區別。文檔也特別棒,因此比較推薦使用這個。

這裏我弄了一個很簡單的項目初始化模板,集成最新穩定版本的electron,以及最新的vue-cli4,另外還添加了normalize.css初始化樣式。electron+vue相關項目的初始化能夠直接使用這個模板,戳這裏web

獲取文件路徑,以及路徑下全部文件

獲取文件路徑,這裏的處理方式,跟以前的翻譯項目(Electron+Vue從零開始打造一個本地文件翻譯器)同樣,經過兩種方式獲取到須要的路徑。vue-cli

  1. 設置input webkitdirectory directory 屬性,而後監聽change事件獲取到所選擇文件夾的路徑
  2. 經過H5的拖拽API監聽drop事件,獲取到 DataTransfer 對象,DataTransfer 對象用於保存拖動而且放下的數據。

這裏不同的是須要對拖入的文件進行判斷,必須拖入的是文件夾。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('已刪除文件');
      }
    },
複製代碼

最後

最近在對象面前裝逼太多了,在對象眼裏我都是發光的存在,她就跟她的同窗講程序猿有多神奇(雖然在我們眼裏是小事),而後她的同窗們都紛紛說要找程序猿男朋友,我內心暗喜,我終於爲我們程序猿爭光了!!!源碼在這 戳這裏

相關文章
相關標籤/搜索