You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

fs.readdirSync执行速度如何?大目录遍历如何优化提速?

大目录下递归遍历文件的优化方案

你的原代码在小目录里没问题,但面对100GB+甚至TB级目录时速度拉胯,核心瓶颈有两个:

  1. 所有IO操作都是同步阻塞的,遍历过程完全串行,大目录下会一直卡着事件循环,没法同时处理多个子目录
  2. 每个文件/目录都单独调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 15:57:18