fs.readdir递归遍历目录,在外层有知道什么时候完成的办法么? 比如统计递归的时间,在结束后做一些事情等等。
随便找个类库,关键词 walkdir 或 globby
@atian25
之前我用了walk
模块,比fs慢了一个数量级。
刚我试了walkdir
模块,跟fs.readdirSync
效率差不多,不过丢文件呐。
我提了issuse:
https://github.com/soldair/node-walkdir/issues/33
我更想知道fs.readdir
遍历目录,有什么方式能够知道它的结束呢?
对比了fs.readdirSync
walkdir
walker
walk
:
windows properties
被遍历的目录包含文件和文件夹数量: 112,033 Files, 14,187 Folders
fs.readdirSync
file:112033; dir:14187; total:126220
22.163s
优点:快
缺点:fs.readdir
不知道如何获得递归的完成时间和事件。
walkdir
callback: async:126128 sync:126134 time:24.664s
emitter: file:111947; dir:14181; path:126128 time:13.454s 优点:速度快,贴近原生api速度。 缺点:漏算文件和目录,不能忍。
walker
file:112033; dir:14188; path:126221;
time:16.550s
优点:统计准确
缺点:异步模式,与walkdir
的emitter对比,这个模块慢了些。
数量统计准确,效率横比也不错,优选。
walk
files:112033; dir:14187 108.304s 优点:统计准确 缺点:太慢
fs.readdir不知道如何获得递归的完成时间和事件
没看懂,递归是你自己做的,每一次调用都有 callback,怎么会拿不到
@atian25 我并不知道目录的广度和深度,也就无法知道什么时候是最后一次递归调用啊 目前只能用readdirSync等它自己同步方式递归完毕。
readdirSync 肯定是不用的,同步的方法性能极低。
你是不是对 readdir 有什么误解,它只是单纯的读取指定目录下的文件列表,并不会自动去读取子级目录,要你自己去发起递归的。
我就是用了递归,才不知道什么时候全部递归遍历完。
递归有出口啊,你在出口统计时间不就行了
了解一下,性能上不敢保证,但是能用,源码挺简单,你看下也能自己写
@axetroy 我上边测试了walk模块,比较慢,不知道你的如何?
aync/await 写法,把异步方法变成同步写法,这样就知道它什么时候执行完了。
function readdir (src) {
return new Promise(function (resolve, reject) {
fs.readdir(src, function (err, files) {
if (err) {
reject(err);
} else {
resolve(files)
}
})
})
}
async function test () {
const files = await readdir('xxxxx/xxxx');
}
node 8后自带promise转换 const {promisify} = require(‘util’);
@dkvirus 我现在用的就是同步的做法,同步只需要用API里的几个sync方法就行了。我想知道有没有办法在异步方式下,还能知道什么时候遍历完。
@fruit-memory 目录深度和广度未知,如何获得递归的出口呢?我就是纠结在这里。
@bi-kai 我给你写的 async/await + Promise 就是异步方式啊,不是什么异步都是在回调函数里处理的,别看它像同步写法,事实上是异步的。
@fulvaz 长见识了,这样确实更方便,我每次还傻乎乎的自己写 Promise,尴尬~
又让我想起了我的老项目: https://github.com/hezedu/sas#demo-使用sas寻找磁盘最深处
glob很快
@dkvirus 哦看错了。如果配合a递归的话,就要嵌套调用你示例中的test方法,我还是没想明白最后一次递归的出口在哪 -_-\\
@bi-kai 说到递归,盗梦空间看过没。
睡着了,做了一个梦; 在这个梦里又做了一个梦,梦里梦,第二层梦; 在第二层梦里又做了一个梦,梦里梦 twice,第三层梦; …
你怎么样才能醒过来?应该是先在第二个梦里醒过来,再在第一个梦里醒过来… 递归的最后一个出口就是第一个梦,细细体会下是不是这个味道…
@waitingsong 后边这个undefined了
@bi-kai 我测试没问题呀。你是js还是ts? EntryType是ts的类型
export const enum EntryType {
unknown = 'unknown',
notExist = 'notExist',
file = 'file',
dir = 'directory',
block = 'blockDevice',
char = 'characterDevice',
fifo = 'fifo',
socket = 'socket',
link = 'symbolicLink',
}
js话可以直接用字符串比较
@bi-kai
js版本的demo
// test.js
const walk = require('rxwalker').default
let dirCount = 0
let fileCount = 0
let entryCout = 0
walk(<path>).subscribe(
val => {
switch (val.type) {
case 'directory':
dirCount += 1
break
case 'file':
fileCount += 1
break
}
if (val.type !== 'notExists') {
entryCout += 1
}
// console.info('got: ', val.type, '----', val.path)
},
err => console.info(err),
() => {
console.info(`complete dirs: ${dirCount}, files: ${fileCount}, entries: ${entryCout} `)
},
)
node test.js
@waitingsong 同一个机器的运行结果:
fs.readdirSync
scan folder finished, dir num:10395, file num:92016, deal time: 00:00:26
rxwalker
scan folder finished, dir num:10396, file num:92016, deal time: 00:01:41 准确性没问题,效率比同步遍历慢。
慢了几倍多。看来异步模式加上 rxjs
事件开销比较大。 不过我这个基于事件流的方式容易实现各种过滤控制(包括文件名、文件父目录名,搜索深度),以及中途终止扫描,扩展也方便(直接pipe流就行了都不用改模块)
10395 vs 10396 相差一个文件
@waitingsong 差的是个文件夹,应该是把传入的根目录算进去了 目前我测的几个模块,walker和你的接近,但都没有中途停止的功能,这是个突出的点。 我目前是一个线程遍历大量目录和文件,更在乎效率,不同业务很可能就需要终止的功能。
对的。传入的路径作为 depth0 计算入目录类型的。
大致看,如果一个入口差异 10ms,10万*10ms= 100s. 同步模式不进入事件循环,在循环效率上比异步高。不过在系统角度看异步不阻塞主线程。各有特点。 我这个模块主要特点是对于循环查询流程可控——可过滤目录入口/文件输出,可控制查询深度,可随时中断流程。 那个 dirFilterCb 回调函数支持的返回值类型不但是同步的(过滤后的)数组,还可以返回 Promise<数组>,当然 Observable<数组>也行,这样花样可多了,你可以拿传入的数据去做异步请求过滤,甚至还可以拿去排序(比如把目录排在前面,文件排在后面)。不过速度比较慢,我看看能否优化下效率,比如提供可选同步查询模式
readdir第一层正常读取,深层目录的递归返回promise,push到一个数组里;然后用Promise.all去执行