fs.readdirSync执行速度如何?大目录遍历如何优化提速?
大目录下递归遍历文件的优化方案
你的原代码在小目录里没问题,但面对100GB+甚至TB级目录时速度拉胯,核心瓶颈有两个:
- 所有IO操作都是同步阻塞的,遍历过程完全串行,大目录下会一直卡着事件循环,没法同时处理多个子目录
- 每个文件/目录都单独调用
fs.statSync,平白多了大量IO请求——其实readdir可以直接返回文件类型,根本不用额外stat
下面是几个实打实的优化方案,从易到难,按需选用:
1. 异步并行遍历 + 减少IO请求(最基础的优化)
改用fs.promises的异步API,配合Promise.all并行处理子目录,同时开启withFileTypes选项直接获取文件类型,省去额外的stat操作:
const fs = require('fs').promises; const path = require('path'); async function getFiles(dir) { const files = []; // 直接获取带类型信息的目录项,不用再stat const dirents = await fs.readdir(dir, { withFileTypes: true }); // 并行处理所有子项,比串行快N倍 await Promise.all(dirents.map(async (dirent) => { const fullPath = path.join(dir, dirent.name); if (dirent.isDirectory()) { // 递归遍历子目录,结果合并到数组 files.push(...await getFiles(fullPath)); } else { files.push(fullPath); } })); return files; }
2. 流式处理,避免内存爆炸(TB级目录必备)
如果目录下有几百万甚至上千万个文件,一次性把所有路径存到数组里会直接撑爆内存。改用流式处理,边遍历边输出,内存占用始终很低:
const fs = require('fs').promises; const path = require('path'); const { Readable } = require('stream'); function getFilesStream(dir) { return new Readable({ objectMode: true, async read() { const dirents = await fs.readdir(dir, { withFileTypes: true }); for (const dirent of dirents) { const fullPath = path.join(dir, dirent.name); if (dirent.isDirectory()) { // 递归生成子目录的流,逐个推送文件 const subStream = getFilesStream(fullPath); subStream.on('data', (file) => this.push(file)); await new Promise(resolve => subStream.on('end', resolve)); } else { this.push(fullPath); } } this.push(null); // 标记流结束 } }); } // 使用示例:逐个处理文件路径 getFilesStream('/path/to/huge/dir') .on('data', (file) => { // 比如写入数据库、做统计,不用等全部遍历完 console.log(file); }) .on('end', () => { console.log('遍历完成'); });
3. 用成熟第三方库(省心又高效)
没必要自己造轮子,像fast-glob这种库已经做了大量性能优化,支持并行遍历、过滤、流式输出,比手写代码靠谱多了:
先安装:
npm install fast-glob
使用示例:
const fg = require('fast-glob'); // 一次性获取所有文件路径(适合文件数量不是特别多的情况) async function getFilesWithGlob(dir) { return fg('**/*', { cwd: dir, onlyFiles: true, absolute: true // 返回绝对路径 }); } // 流式遍历(适合超大目录) async function getFilesStreamWithGlob(dir) { const stream = fg.stream('**/*', { cwd: dir, onlyFiles: true, absolute: true }); // 逐个处理文件 for await (const file of stream) { console.log(file); } }
4. 调用系统原生命令(性能天花板)
对于超大规模目录,直接用系统自带的find(Linux/macOS)或dir(Windows)命令,比Node.js自己遍历快得多——毕竟系统工具是底层实现,IO效率拉满:
const { spawn } = require('child_process'); function getFilesWithSystemCmd(dir) { return new Promise((resolve, reject) => { const files = []; // Linux/macOS 用find命令,只找文件 const proc = spawn('find', [dir, '-type', 'f']); proc.stdout.on('data', (data) => { // 把输出按行分割,过滤空行 files.push(...data.toString().split('\n').filter(Boolean)); }); proc.stderr.on('data', (err) => { reject(err.toString()); }); proc.on('close', () => { resolve(files); }); }); }
这个方案唯一的缺点是跨平台兼容性差,但性能是所有方案里最高的,适合服务器环境下的超大目录遍历。
内容的提问来源于stack exchange,提问作者Omi in a hellcat
相关产品推荐
相关产品推荐

