如何在Node.js的目录中递归获取所有文件夹名称的列表?

时间:2019-05-10 20:12:59

标签: javascript node.js directory-listing

我试图递归获取目录中存在的所有文件夹的列表。然后,我想获取每个文件夹中所有文件夹的列表。

我想继续递归执行此操作。我该怎么办?

const folder = 'folder1/';
const fs = require('fs');

fs.readdir(folder, (err, files) => {
  files.forEach(file => {
    // detect folder and continue to look for sub-folders
  });
});

4 个答案:

答案 0 :(得分:2)

没有错误检查或承诺,这是一种快速解决方案:

const folder = 'test/';
const fs = require('fs');

recurseFolder(folder);

function recurseFolder (folder_top) {
  fs.readdir(folder_top, (err, things) => {
    things.forEach(thing => {
      const full_path = folder_top + thing;
      fs.stat(full_path, (error, stat) => {
        if (stat.isFile()) {
          console.log(thing);
        } else if (stat.isDirectory()) {
          console.log(thing + "*");
          recurseFolder(full_path + '/');
        }
      });
    });
  });
}

答案 1 :(得分:1)

这是一个使用Promises递归查找所有子目录的实现:

const fs = require('fs');
const path = require('path');

// Uncomment polyfill if using Node.js < 10.x
// const util = require('util');
// fs.promises = {
//     readdir: util.promisify(fs.readdir),
//     stat: util.promisify(fs.stat)
// };

async function listDirectories(dir) {
    const fileNames = await fs.promises.readdir(dir);

    // Create a directory listing
    const listings = await Promise.all(
        fileNames.map(async (name) => {
            const file = path.join(dir, name);
            const stat = await fs.promises.stat(file);
            return {file, stat};
        })
    );

    // Create a list of all the sub directories using the listings
    const subDirs = listings.filter((listing) => listing.stat.isDirectory())
        .map((listing) => listing.file);

    // Recurse over the sub directories and add their sub directories to the list of directories
    const subSubDirs = await Promise.all(subDirs.map(listDirectories));
    return subSubDirs.reduce((a, b) => a.concat(b), subDirs);
}

listDirectories('folder1/').then((dirs) => {
    console.log(dirs);
}).catch((err) => {
    process.exitCode = 1;
    console.error(err);
});

与等效的基于回调的解决方案相比,我认为基于promise的解决方案更易于理解并且更不易出错:

const fs = require('fs');
const path = require('path');

function listDirectories(dir, callback) {
    fs.readdir(dir, (err, fileNames) => {
        if (err) return callback(err);
        if (!fileNames.length) return callback(null, []);

        // We have to keep track of the remaining operations
        let remaining = fileNames.length;

        const subDirs = [];
        fileNames.forEach((name) => {
            const file = path.join(dir, name);
            fs.stat(file, (err, stats) => {
                if (err) return callback(err);

                if (stats.isDirectory()) {
                    subDirs.push(file);
                    listDirectories(file, (err, subSubDirs) => {
                        if (err) return callback(err);
                        subDirs.push(...subSubDirs);
                        if (!--remaining) {
                            // We've gathered the sub dirs of this sub dir and this was the last file to check, all done.
                            callback(null, subDirs);
                        }
                    });
                } else if (!--remaining) {
                    // File was not a dir and was the last file to check, all done.
                    callback(null, subDirs);
                }
            });
        });
    });
}

listDirectories('folder1/',(err, dirs) => {
    if (err) {
        process.exitCode = 1;
        console.error(err);
    } else {
        console.log(dirs);
    }
});

答案 2 :(得分:0)

-99级:回调地狱

这里令人窒息,我需要呼吸一下。通过使用fs.promises和较新的async-await语法,我们可以轻松了解-

// main.js

const { readdir, stat } =
  require ('fs') .promises

const { join } =
  require ('path')

const dirs = async (path = ".") =>
  (await stat (path)) .isDirectory ()
    ? Promise
        .all
          ( (await readdir (path))
              .map (p => dirs (join (path, p)))
          )
        .then
          ( results =>
              [] .concat (path, ...results)
          )
    : []

dirs (process.argv[2]) .then (console.log, console.error)

在终端中,我安装了一个示例软件包,然后在我们项目的目录中测试该程序-

$ npm install ramda
$ node main.js .

[ '.'
, 'node_modules'
, 'node_modules/ramda'
, 'node_modules/ramda/dist'
, 'node_modules/ramda/es'
, 'node_modules/ramda/es/internal'
, 'node_modules/ramda/src'
, 'node_modules/ramda/src/internal'
]

我们可以添加一个depth参数,该参数控制dirs重复出现的深度-

// main.js

const { readdir, stat } =
  require ('fs') .promises

const { join } =
  require ('path')

const dirs = async (path = ".", depth = Infinity) =>
  (await stat (path)) .isDirectory ()
    ? depth === -1
        ? []
        : Promise
            .all
              ( (await readdir (path))
                  .map (p => dirs (join (path, p), depth - 1))
              )
            .then
              ( results =>
                  [] .concat (path, ...results)
              )
    : []

dirs (process.argv[2], process.argv[3]) .then (console.log, console.error)

再次在终端中进行测试,我们显示了各种路径和深度-

$ node main.js . 1
[ '.'
, 'node_modules'
]


$ node main.js . 2
[ '.'
, 'node_modules'
, 'node_modules/ramda'
]

$ node main.js node_modules/ 1
[ 'node_modules/'
, 'node_modules/ramda'
]

$ node main.js node_modules/ 2
[ 'node_modules/'
, 'node_modules/ramda'
, 'node_modules/ramda/dist'
, 'node_modules/ramda/es'
, 'node_modules/ramda/src'
]

进阶

这个问题与我回答here的另一个问题有关。那里的答案使用了多种技术来提供完整的解决方案,但接着显示可以提取和重用某些模式。

在上面的dirs解决方案中,我们只有一个功能要检查,因此我们无法识别模式。但是,我将说明如何将另一个答案Parallel中出现的可重用模块也应用于此dirs-

// main.js

const { readdir, stat } =
  require ('fs') .promises

const { join } =
  require ('path')

const Parallel =
  require ('./parallel')

const dirs = async (path = ".", depth = Infinity) =>
  (await stat (path)) .isDirectory ()
    ? depth === -1
        ? []
        : Parallel (readdir (path))
            .flatMap (f => dirs (join (path, f), depth - 1))
            .then (results => [ path, ...results ])
    : []

dirs (process.argv[2], process.argv[3]) .then (console.log, console.error)

点击上面的链接,了解如何使用Parallel完成类似的任务

答案 3 :(得分:-1)

您可以使用IsFileIsDirectory

files.forEach(file => {
    fs.stat(file, function (error, stat) {
      if (stat.isFile()) // it's a file
      if (stat.isDirectory()) // it's a directory
    });
  });