关于使用fs.readdir进行异步目录搜索有什么想法吗?我意识到我们可以引入递归,并调用read目录函数来读取下一个目录,但我有点担心它不是异步的…

什么好主意吗?我已经看了node-walk,它很棒,但它不能像readdir那样只给我数组中的文件。虽然

寻找这样的输出…

['file1.txt', 'file2.txt', 'dir/file3.txt']

当前回答

使用node-dir可以生成您想要的输出

var dir = require('node-dir');

dir.files(__dirname, function(err, files) {
  if (err) throw err;
  console.log(files);
  //we have an array of files now, so now we can iterate that array
  files.forEach(function(path) {
    action(null, path);
  })
});

其他回答

我喜欢上面chjj的答案,如果没有那个开始,我就无法创建我的并行循环版本。

var fs = require("fs");

var tree = function(dir, done) {
  var results = {
        "path": dir
        ,"children": []
      };
  fs.readdir(dir, function(err, list) {
    if (err) { return done(err); }
    var pending = list.length;
    if (!pending) { return done(null, results); }
    list.forEach(function(file) {
      fs.stat(dir + '/' + file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          tree(dir + '/' + file, function(err, res) {
            results.children.push(res);
            if (!--pending){ done(null, results); }
          });
        } else {
          results.children.push({"path": dir + "/" + file});
          if (!--pending) { done(null, results); }
        }
      });
    });
  });
};

module.exports = tree;

我也创建了一个Gist。欢迎评论。我仍然在NodeJS领域起步,所以这是我希望学到更多的一种方式。

另一个答案,但这次使用的是TypeScript:

/** * Recursively walk a directory asynchronously and obtain all file names (with full path). * * @param dir Folder name you want to recursively process * @param done Callback function, returns all files with full path. * @param filter Optional filter to specify which files to include, * e.g. for json files: (f: string) => /.json$/.test(f) */ const walk = ( dir: string, done: (err: Error | null, results ? : string[]) => void, filter ? : (f: string) => boolean ) => { let results: string[] = []; fs.readdir(dir, (err: Error, list: string[]) => { if (err) { return done(err); } let pending = list.length; if (!pending) { return done(null, results); } list.forEach((file: string) => { file = path.resolve(dir, file); fs.stat(file, (err2, stat) => { if (stat && stat.isDirectory()) { walk(file, (err3, res) => { if (res) { results = results.concat(res); } if (!--pending) { done(null, results); } }, filter); } else { if (typeof filter === 'undefined' || (filter && filter(file))) { results.push(file); } if (!--pending) { done(null, results); } } }); }); }); };

我最近编写了这个代码,并认为在这里分享它是有意义的。代码使用了异步库。

var fs = require('fs');
var async = require('async');

var scan = function(dir, suffix, callback) {
  fs.readdir(dir, function(err, files) {
    var returnFiles = [];
    async.each(files, function(file, next) {
      var filePath = dir + '/' + file;
      fs.stat(filePath, function(err, stat) {
        if (err) {
          return next(err);
        }
        if (stat.isDirectory()) {
          scan(filePath, suffix, function(err, results) {
            if (err) {
              return next(err);
            }
            returnFiles = returnFiles.concat(results);
            next();
          })
        }
        else if (stat.isFile()) {
          if (file.indexOf(suffix, file.length - suffix.length) !== -1) {
            returnFiles.push(filePath);
          }
          next();
        }
      });
    }, function(err) {
      callback(err, returnFiles);
    });
  });
};

你可以这样使用它:

scan('/some/dir', '.ext', function(err, files) {
  // Do something with files that ends in '.ext'.
  console.log(files);
});

简单,基于异步承诺


const fs = require('fs/promises');
const getDirRecursive = async (dir) => {
    try {
        const items = await fs.readdir(dir);
        let files = [];
        for (const item of items) {
            if ((await fs.lstat(`${dir}/${item}`)).isDirectory()) files = [...files, ...(await getDirRecursive(`${dir}/${item}`))];
            else files.push({file: item, path: `${dir}/${item}`, parents: dir.split("/")});
        }
        return files;
    } catch (e) {
        return e
    }
};

用法:await getDirRecursive("./public");

使用承诺(Q)以函数式风格解决此问题:

var fs = require('fs'),
    fsPath = require('path'),
    Q = require('q');

var walk = function (dir) {
  return Q.ninvoke(fs, 'readdir', dir).then(function (files) {

    return Q.all(files.map(function (file) {

      file = fsPath.join(dir, file);
      return Q.ninvoke(fs, 'lstat', file).then(function (stat) {

        if (stat.isDirectory()) {
          return walk(file);
        } else {
          return [file];
        }
      });
    }));
  }).then(function (files) {
    return files.reduce(function (pre, cur) {
      return pre.concat(cur);
    });
  });
};

它返回一个数组的promise,所以你可以这样使用它:

walk('/home/mypath').then(function (files) { console.log(files); });