多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
*遍历算法* * * * * * 目录是一个树状结构,在遍历时一般使用深度优先+先序遍历算法。深度优先,意味着到达一个节点后,首先接着遍历子节点而不是邻居节点。先序遍历,意味着首次到达了某节点就算遍历完成,而不是最后一次返回某节点才算数。因此使用这种遍历方式时,下边这棵树的遍历顺序是`A > B > D > E > C > F`。 ``` A / \ B C / \ \ D E F ``` *同步遍历* * * * * * 了解了必要的算法后,我们可以简单地实现以下目录遍历函数。 ``` function travel(dir, callback) { fs.readdirSync(dir).forEach((file) => { let pathname = path.join(dir, file) if (fs.statSync(pathname).isDirectory()) { travel(pathname, callback) } else { callback(pathname) } }) } ``` 可以看到,该函数以某个目录作为遍历的起点。遇到一个子目录时,就先接着遍历子目录。遇到一个文件时,就把文件的绝对路径传给回调函数。回调函数拿到文件路径后,就可以做各种判断和处理。因此假设有以下目录: ![](https://box.kancloud.cn/d2523ba5cf18d6d160830cdebe28528b_476x465.png) *异步遍历* * * * * * 如果读取目录或读取文件状态时使用的是异步API,目录遍历函数实现起来会有些复杂,但原理完全相同。`travel` 函数的异步版本如下。 ``` let fs = require('fs') let path = require('path') function travel(dir, callback, finish) { // 调用fs异步API, 接受目录参数和回调函数 fs.readdir(dir, function(err, files) { // 创建一个自执行函数, files是dir下所有文件 // 传入参数为0, 因为files是一个数组, 包含dir下所有文件的文件名数组 (function next(i) { console.log(i, '-', dir) // 进来首先判断当前i是不是完成对dir下所有文件的遍历 if (i < files.length) { // 取得文件的绝对路径 let pathname = path.join(dir, files[i]) // fs.stat 取得文件信息 fs.stat(pathname, function(err, stats) { // 判断当前文件是否为文件夹 if (stats.isDirectory()) { // 如果当前文件是文件夹, 则重新调用travel // 遵循深度优先原则, 先遍历深度 // 此时finish函数就是next函数 -> function () { next(i + 1) } // 此时dir就是一个新的文件夹路径, callback不变, 遍历之后调用finish函数 travel(pathname, callback, function() { next(i + 1) }) } else { // 当此文件夹再没有文件夹的时候 // 调用callback函数, 第一个参数是文件的绝对路径 // 第二个参数是next函数, 外面必须调用此函数, 来进行下一步遍历 callback(pathname, function() { next(i + 1) }) } // 注意:每个文件夹都会生成一个next函数 // 而且是自执行的, 所以并不需要控制next函数返回上一层目录 }) // 如果已经完成对dir下所有文件夹里的文件遍历, 即i >= files.length } else { finish && finish() } })(0) }) } travel(path.join(__dirname), function(pathname, next) { console.log(pathname) next() }, function() { console.log('完成') }) ``` ![](https://box.kancloud.cn/6f09566c9969279a74a518a80784fc22_581x252.png)