The origin of the requirement is when the object is sorting out the sounds, she collects the sound resources have nested subfolders, but she wants to bring all the files to the level 1 directory, before the program, she does this:
- Copies files in subfolders to a level 1 directory
- Deleting a subfolder
If you have fewer subfolders, that’s not a lot of work, but if you have deeper subfolders, that’s a lot of work, and it’s a lot of repetitive work. So she came to me for help. I happened to be learning Node recently, so I could practice node.
The implementation process
The body of the implementation process is a recursive, traversing the folder, if it is a file to perform the transfer operation, not to pass the new folder path, continue traversing.
The specific implementation
Specific implementation of the following steps:
- Project initialization
- Obtain the file path and all files in the file path
- Transfer operation
- Delete operation
Project initialization
Since the platform is Windows and the interface cannot be ugly, the technology is chosen as Electron + Node. Even though electron packs 50 MB of files, the benefits of the software far outweigh the costs. As for the electron vue integration, I previously wrote about electron + Vue building a local music player from scratch. Electron and VUE integration currently has two solutions for the community:
- SimulatedGREG/ electronic-Vue, this one is older and bloated, but if it’s a migration from an older project, consider using it.
- Nklayman/Vue-CLI-plugin-electron – Builder, this code supports the latest stable version of electron and the latest vUE scaffolding, the main process of the code is integrated in backgroud. js, other code organization is the same as the vUE project. The documentation is great, so I recommend this one.
Here I made a very simple project initialization template, integrating the latest stable version of electron and the latest Vue-CLI4, plus adding the normalize.css initialization style. Electron +vue related items can be initialized directly using this template, stamped here.
Obtain the file path and all files in the file path
Obtaining the file path is handled in the same way as in the previous translation project (Electron+Vue builds a local file translator from scratch). There are two ways to obtain the desired path.
- Set the Input WebKITDirectory Directory property, and listen for the change event to get the path to the selected folder
- H5’s drag and drop API listens for drop events and gets a DataTransfer object that holds the data that you drag and drop.
What’s different here is that you need to judge the file you’re dragging in, and you have to drag in the folder.
const originFiles = [...e.dataTransfer.files]; const isAllDir = originFiles.every(file => fs.statSync(file.path).isDirectory() ); if (! isAllDir) { ipcRenderer.send("confirmDialog"); return false; }Copy the code
The main process
Ipcmain. on("confirmDialog", () => {dialog.showmessageBox ({type: "info", title: "confirm ", message: "Please confirm whether the selected files are folders"}); });Copy the code
Obtain all files in the directory
Async getAllFiles(path) {try {const res = await fsp.readdir(path); return res; } catch (error) { console.log(error); }},Copy the code
Transfer operation
The fs module’s rename method is used to determine whether the folder is a folder to determine whether the recursive operation is required. For files with the same name, the system determines whether the size of the files to be transferred is consistent with that of the files in the destination folder. If not, the system renames the files. Renaming requires generating a file ID with the file name following it to ensure that the file is not named the same. The specific code is as follows:
Determine whether it is a folder
Async isDir(path) {try {const res = await fsp.stat(path); if (res.isDirectory()) { return true; } else { return false; } } catch (error) { console.log(error); }},Copy the code
Generated file ID
/ / generated file id uuid (len, radix) {const chars. = '0123456789 abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz 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'; 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(''); },Copy the code
Transfer operation
/* dirPath: original folder targetPath: New target folder */ 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 not, copy it, if not, recurse if (! IsDir) {// Check whether the destination folder has the same file name console.log(' traversed all files '); console.log({ filePath, targetFilePath }); If (fs.existssync (targetFilePath)) {console.log(' the target file ${file} exists in the target folder ${targetDirPath} '); console.log({ filePath, targetFilePath }); const targetFiles = await this.getAllFiles(targetDirPath); const fileIndex = targetFiles.indexOf(file); Console. log(' ${file} subscript ${fileIndex} '); Const targetFileInfo = await fsp.stat(targetFilePath); const originFileInfo = await fsp.stat(filePath); Console. log(' Size of originFileinfo.size} ${targetFileinfo.size} '); If (fileIndex >= 0 && OriginFileInfo. size! == targetFileinfo.size) {// Get the name of the original file and its suffix const fileExt = path.extName (filePath); const fileName = path.basename(filePath, fileExt); // generate a newFileName const newFileName = '${fileName}-${this.uuid(6, 16)}'; const newPath = path.resolve(dirPath, `${newFileName}${fileExt}`); const newTargetFilePath = path.resolve(targetDirPath, `${newFileName}${fileExt}`); // Rename await fsp.rename(filePath, newPath); // rename await fsp.rename(filePath, newPath); // Move to the new target folder await fsp.rename(newPath, newTargetFilePath); // Move to the new target folder await fsp.rename(newPath, newTargetFilePath); } else {console.log(' the target file has the same name but different contents '); console.log({ filePath, targetFilePath }); Fsp.rename (filePath, targetFilePath); }} else {console.log(' the destination file does not exist in the destination folder '); await fsp.rename(filePath, targetFilePath); }} else {// is a directory and executes await this.moveFiles(filePath, targetDirPath); // is a directory and executes await this.moveFiles(filePath, targetDirPath); } let timer = setTimeout(async () => { await this.removeDir(dirPath); this.loading = false; clearTimeout(timer); }, 1000); }); },Copy the code
Delete operation
Because the original folder still exists after the copy move method is executed, it needs to be deleted. If it is a file, run the fs.unlinkSync method. If it is a directory, run the recursive method. Finally, delete the directory and ensure that only the directory is left. The specific code is as follows
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(' detected directory ${curPath} '); this.removeDir(curPath); } else { fs.unlinkSync(curPath); Console. log(' deleted file ${curPath} '); }}); // Delete the folder fs.rmdirsync (URL); } else {console.log(' deleted file '); }},Copy the code
The last
Recently in front of the object of the outfit force too much, in the object of the eye I are the existence of light, she told her classmates program ape has much magic (although in our eyes is small), and then her classmates have said to find a program ape boyfriend, MY heart secretly happy, I finally for our program ape win light!! The source code is poked here