乐趣区

关于electron:ElectronVueNode-实现展平文件夹小工具

需要起源是对象整顿音效的时候,她收集的音效资源是有嵌套的子文件夹的,然而她想把所有的文件都提到一级目录,没有程序之前,她的操作是:

  1. 复制子文件夹中的文件到一级目录
  2. 删除子文件夹

如果子文件夹少,那工作量也还算不大,然而如果子文件夹嵌套较深,这工作量就上来了,而且都是反复的工作。于是她过去寻求我的帮忙,我一看刚好本人最近不是在学习 node 吗,这刚好能够练习下 node。

实现流程

实现流程主体是一个递归,遍历文件夹,如果是文件就执行转移操作,不是就传入新的文件夹门路,持续遍历。

具体实现

具体实现分以下几步:

  1. 我的项目初始化
  2. 获取文件门路, 以及门路下所有文件
  3. 转移操作
  4. 删除操作

我的项目初始化

因为应用平台为 windows,而后又不能把界面做丑,所以技术抉择为 Electron + Node。尽管 electron 打包进去文件有 50M, 然而软件带来的收益是远远大于须要付出的代价的。对于 electron 与 vue 的集成,之前写过文章 Electron+vue 从零开始打造一个本地音乐播放器。electron 与 vue 集成目前社区有两个计划:

  1. SimulatedGREG/electron-vue, 这个比拟老,比拟臃肿,不过如果是老我的项目的迁徙的话,能够思考应用。
  2. nklayman/vue-cli-plugin-electron-builder, 这个反对最新稳固版本的 electron 以及最新的 vue 脚手架,代码里主过程相干代码集成在 background.js 中,其余代码组织跟写 vue 我的项目没有区别。文档也特地棒,所以比拟举荐应用这个。

这里我弄了一个很简略的我的项目初始化模板,集成最新稳固版本的 electron, 以及最新的 vue-cli4, 另外还增加了 normalize.css 初始化款式。electron+vue 相干我的项目的初始化能够间接应用这个模板, 戳这里。

获取文件门路, 以及门路下所有文件

获取文件门路 ,这里的解决形式,跟之前的翻译我的项目(Electron+Vue 从零开始打造一个本地文件翻译器) 一样,通过两种形式获取到须要的门路。

  1. 设置 input webkitdirectory directory 属性,而后监听 change 事件获取到所抉择文件夹的门路
  2. 通过 H5 的拖拽 API 监听 drop 事件,获取到 DataTransfer 对象,DataTransfer 对象用于保留拖动并且放下的数据。

这里不一样的是须要对拖入的文件进行判断,必须拖入的是文件夹。

      const originFiles = [...e.dataTransfer.files];
      const isAllDir = originFiles.every(file =>
        fs.statSync(file.path).isDirectory());

      if (!isAllDir) {ipcRenderer.send("confirmDialog");
        return false;
      }

主过程

ipcMain.on("confirmDialog", () => {
  dialog.showMessageBox({
    type: "info",
    title: "确认",
    message: "请确认抉择的文件是否都是文件夹"
  });
});

获取门路下所有的文件

    // 获取文件门路下的所有文件
    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('已删除文件');
      }
    },

最初

最近在对象背后装逼太多了,在对象眼里我都是发光的存在,她就跟她的同学讲程序猿有多神奇 (尽管在咱们眼里是小事),而后她的同学们 都纷纷说要找程序猿男友,我心里暗喜,我终于为咱们程序猿抹黑了!!!源码在这 戳这里

退出移动版