Node.js fs.readdir рекурсивный поиск в каталоге

Любые идеи поиска в async-каталоге с использованием fs.readdir? Я понимаю, что мы могли бы ввести рекурсию и вызвать функцию чтения в следующей директории для чтения, но я немного обеспокоен тем, что она не является асинхронной...

Любые идеи? Я смотрел на node-walk, что здорово, но не дает мне только файлы в массиве, например readdir. Хотя

Ищем выход, например...

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

Ответ 1

Есть два основных способа сделать это. В асинхронной среде вы заметите, что есть два вида циклов: последовательный и параллельный. Последовательный цикл ожидает завершения одной итерации, прежде чем перейти к следующей итерации - это гарантирует, что каждая итерация цикла завершается по порядку. В параллельном цикле все итерации запускаются одновременно, и одну можно завершить раньше другой, однако это намного быстрее, чем последовательный цикл. Так что в этом случае, вероятно, лучше использовать параллельный цикл, потому что не имеет значения, в каком порядке выполняется обход, до тех пор, пока он завершает и возвращает результаты (если вы не хотите, чтобы они были в порядке).

Параллельный цикл будет выглядеть так:

var fs = require('fs');
var path = require('path');
var walk = function(dir, done) {
  var results = [];
  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) {
      file = path.resolve(dir, file);
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            results = results.concat(res);
            if (!--pending) done(null, results);
          });
        } else {
          results.push(file);
          if (!--pending) done(null, results);
        }
      });
    });
  });
};

Последовательный цикл будет выглядеть следующим образом:

var fs = require('fs');
var path = require('path');
var walk = function(dir, done) {
  var results = [];
  fs.readdir(dir, function(err, list) {
    if (err) return done(err);
    var i = 0;
    (function next() {
      var file = list[i++];
      if (!file) return done(null, results);
      file = path.resolve(dir, file);
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            results = results.concat(res);
            next();
          });
        } else {
          results.push(file);
          next();
        }
      });
    })();
  });
};

И протестировать его в своем домашнем каталоге (ВНИМАНИЕ: список результатов будет огромным, если в вашем домашнем каталоге много вещей):

walk(process.env.HOME, function(err, results) {
  if (err) throw err;
  console.log(results);
});

ОБНОВЛЕНИЕ: улучшенные примеры.

Ответ 2

На всякий случай, если кто-либо найдет это полезным, я также собрал синхронную версию.

var walk = function(dir) {
    var results = [];
    var list = fs.readdirSync(dir);
    list.forEach(function(file) {
        file = dir + '/' + file;
        var stat = fs.statSync(file);
        if (stat && stat.isDirectory()) { 
            /* Recurse into a subdirectory */
            results = results.concat(walk(file));
        } else { 
            /* Is a file */
            results.push(file);
        }
    });
    return results;
}

Совет. Использовать меньше ресурсов при фильтрации. Фильтр внутри этой функции. Например, Заменить results.push(file); с кодом ниже. При необходимости отрегулируйте:

    file_type = file.split(".").pop();
    file_name = file.split(/(\\|\/)/g).pop();
    if (file_type == "json") results.push(file);

Ответ 3

A. Посмотрите на файловый модуль. Он имеет функцию walk:

file.walk(начало, обратный вызов)

Навигает дерево файлов, вызывая обратный вызов для каждого каталога, проходящий (null, dirPath, dirs, files).

Это может быть для вас! И да, это асинхронно. Однако, я думаю, вам нужно было бы заполнить полный путь самостоятельно, если бы они вам были нужны.

B. Альтернатива и даже один из моих фаворитов: используйте для этого unix find. Зачем что-то еще, что уже запрограммировано? Возможно, не совсем то, что вам нужно, но все же стоит проверить:

var execFile = require('child_process').execFile;
execFile('find', [ 'somepath/' ], function(err, stdout, stderr) {
  var file_list = stdout.split('\n');
  /* now you've got a list with full path file names */
});

Find имеет хороший встроенный механизм кеширования, который делает последующие поиски очень быстрыми, если только несколько папок изменились.

Ответ 4

В нем используется максимальное количество новых, модных слов, доступных в узле 8, включая обещания, утилиту/обещание, деструктуризацию, асинхронное ожидание, отображение + уменьшение и многое другое, что заставляет ваших коллег ломать голову, пытаясь выяснить, что продолжается.

Узел 8+

Нет внешних зависимостей.

const { promisify } = require('util');
const { resolve } = require('path');
const fs = require('fs');
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);

async function getFiles(dir) {
  const subdirs = await readdir(dir);
  const files = await Promise.all(subdirs.map(async (subdir) => {
    const res = resolve(dir, subdir);
    return (await stat(res)).isDirectory() ? getFiles(res) : res;
  }));
  return files.reduce((a, f) => a.concat(f), []);
}

Usage

Usage
getFiles(__dirname)
  .then(files => console.log(files))
  .catch(e => console.error(e));

Узел 10. 10+

Обновлен для узла 10+ с еще большим количеством ударов:

const { resolve } = require('path');
const { readdir } = require('fs').promises;

async function getFiles(dir) {
  const dirents = await readdir(dir, { withFileTypes: true });
  const files = dirents.map((dirent) => {
    const res = resolve(dir, dirent.name);
    return dirent.isDirectory() ? getFiles(res) : res;
  });
  return Array.prototype.concat(...files);
}

Обратите внимание, что начиная с узла 11.15.0, вы можете использовать files.flat() вместо Array.prototype.concat(...files) для выравнивания массива файлов.

Узел 11+

Если вы хотите полностью взорвать всех, вы можете использовать следующую версию, используя асинхронные итераторы. Помимо того, что он действительно крут, он также позволяет потребителям получать результаты по одному, что делает его более подходящим для действительно больших каталогов.

const { resolve } = require('path');
const { readdir } = require('fs').promises;

async function* getFiles(dir) {
  const dirents = await readdir(dir, { withFileTypes: true });
  for (const dirent of dirents) {
    const res = resolve(dir, dirent.name);
    if (dirent.isDirectory()) {
      yield* getFiles(res);
    } else {
      yield res;
    }
  }
}

Использование изменилось, поскольку тип возвращаемого значения теперь является асинхронным итератором, а не обещанием

(async () => {
  for await (const f of getFiles('.')) {
    console.log(f);
  }
})()

На случай, если кому-то интересно, я написал больше об асинхронных итераторах здесь: https://qwtel.com/posts/software/async-generators-in-the-wild/

Ответ 5

Другим приятным пакетом npm является glob.

npm install glob

Он очень мощный и должен охватывать все ваши рекурсивные потребности.

Edit:

На самом деле я не был полностью доволен glob, поэтому я создал readdirp.

Я очень уверен, что его API позволяет найти файлы и каталоги рекурсивно и очень легко применять определенные фильтры.

Прочитайте его документацию, чтобы лучше понять, что он делает и устанавливает через:

npm install readdirp

Ответ 6

Я рекомендую использовать node-glob для выполнения этой задачи.

var glob = require( 'glob' );  

glob( 'dirname/**/*.js', function( err, files ) {
  console.log( files );
});

Ответ 7

Если вы хотите использовать пакет npm, гаечный ключ довольно хорош.

var wrench = require("wrench");

var files = wrench.readdirSyncRecursive("directory");

wrench.readdirRecursive("directory", function (error, files) {
    // live your dreams
});

EDIT (2018):
Любой, кто читает в последнее время: автор устарел этот пакет в 2015 году:

wrench.js устарел и не был обновлен довольно долго. Я настоятельно рекомендую использовать fs-extra для выполнения каких-либо дополнительных операций с файловой системой.

Ответ 8

Я любил ответ от 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, так что я надеюсь узнать больше.

Ответ 9

Используйте 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);
  })
});

Ответ 10

С рекурсией

var fs = require('fs')
var path = process.cwd()
var files = []

var getFiles = function(path, files){
    fs.readdirSync(path).forEach(function(file){
        var subpath = path + '/' + file;
        if(fs.lstatSync(subpath).isDirectory()){
            getFiles(subpath, files);
        } else {
            files.push(path + '/' + file);
        }
    });     
}

Вызов

getFiles(path, files)
console.log(files) // will log all files in directory

Ответ 11

Я недавно закодировал это и подумал, что имеет смысл поделиться этим здесь. В коде используется асинхронная библиотека.

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);
});

Ответ 12

Другим вариантом является библиотека с именем Filehound. Он будет рекурсивно искать данный каталог (рабочий каталог по умолчанию). Он поддерживает различные фильтры, обратные вызовы, promises и синхронизацию.

Например, найдите текущий рабочий каталог для всех файлов (используя обратные вызовы):

const Filehound = require('filehound');

Filehound.create()
.find((err, files) => {
    if (err) {
        return console.error(`error: ${err}`);
    }
    console.log(files); // array of files
});

Или promises и указав конкретный каталог:

const Filehound = require('filehound');

Filehound.create()
.paths("/tmp")
.find()
.each(console.log);

Обратитесь к документам для дальнейших примеров использования и примеров использования: https://github.com/nspragg/filehound

Отказ от ответственности: я автор.

Ответ 13

Используя async/await, это должно работать:

const FS = require('fs');
const readDir = promisify(FS.readdir);
const fileStat = promisify(FS.stat);

async function getFiles(dir) {
    let files = await readDir(dir);

    let result = files.map(file => {
        let path = Path.join(dir,file);
        return fileStat(path).then(stat => stat.isDirectory() ? getFiles(path) : path);
    });

    return flatten(await Promise.all(result));
}

function flatten(arr) {
    return Array.prototype.concat(...arr);
}

Вы можете использовать bluebird.Promisify или это:

/**
 * Returns a function that will wrap the given 'nodeFunction'. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument.
 *
 * @param {Function} nodeFunction
 * @returns {Function}
 */
module.exports = function promisify(nodeFunction) {
    return function(...args) {
        return new Promise((resolve, reject) => {
            nodeFunction.call(this, ...args, (err, data) => {
                if(err) {
                    reject(err);
                } else {
                    resolve(data);
                }
            })
        });
    };
};

Узел 8+ имеет встроенную функцию Promisify

Смотрите мой другой ответ для генератора подход, который может дать результаты еще быстрее.

Ответ 14

Ознакомьтесь с библиотекой final-fs. Он предоставляет функцию readdirRecursive:

ffs.readdirRecursive(dirPath, true, 'my/initial/path')
    .then(function (files) {
        // in the `files` variable you've got all the files
    })
    .otherwise(function (err) {
        // something went wrong
    });

Ответ 15

Реализация автономных обещаний

Я использую if.js в этом примере.

var fs = require('fs')
, path = require('path')
, when = require('when')
, nodefn = require('when/node/function');

function walk (directory, includeDir) {
    var results = [];
    return when.map(nodefn.call(fs.readdir, directory), function(file) {
        file = path.join(directory, file);
        return nodefn.call(fs.stat, file).then(function(stat) {
            if (stat.isFile()) { return results.push(file); }
            if (includeDir) { results.push(file + path.sep); }
            return walk(file, includeDir).then(function(filesInDir) {
                results = results.concat(filesInDir);
            });
        });
    }).then(function() {
        return results;
    });
};

walk(__dirname).then(function(files) {
    console.log(files);
}).otherwise(function(error) {
    console.error(error.stack || error);
});

Я включил необязательный параметр includeDir, который будет включать каталоги в списке файлов, если установлено значение true.

Ответ 17

Здесь еще одна реализация. Ни одно из вышеперечисленных решений не имеет ограничений, поэтому, если ваша структура каталогов велика, все они будут разбиты и в конечном итоге исчерпаны ресурсы.

var async = require('async');
var fs = require('fs');
var resolve = require('path').resolve;

var scan = function(path, concurrency, callback) {
    var list = [];

    var walker = async.queue(function(path, callback) {
        fs.stat(path, function(err, stats) {
            if (err) {
                return callback(err);
            } else {
                if (stats.isDirectory()) {
                    fs.readdir(path, function(err, files) {
                        if (err) {
                            callback(err);
                        } else {
                            for (var i = 0; i < files.length; i++) {
                                walker.push(resolve(path, files[i]));
                            }
                            callback();
                        }
                    });
                } else {
                    list.push(path);
                    callback();
                }
            }
        });
    }, concurrency);

    walker.push(path);

    walker.drain = function() {
        callback(list);
    }
};

Использование concurrency из 50 работает очень хорошо и почти так же быстро, как и более простые реализации для небольших структур каталогов.

Ответ 19

Я изменил Тревор-старший обещание на основе Bluebird

var fs = require('fs'),
    path = require('path'),
    Promise = require('bluebird');

var readdirAsync = Promise.promisify(fs.readdir);
var statAsync = Promise.promisify(fs.stat);
function walkFiles (directory) {
    var results = [];
    return readdirAsync(directory).map(function(file) {
        file = path.join(directory, file);
        return statAsync(file).then(function(stat) {
            if (stat.isFile()) {
                return results.push(file);
            }
            return walkFiles(file).then(function(filesInDir) {
                results = results.concat(filesInDir);
            });
        });
    }).then(function() {
        return results;
    });
}

//use
walkDir(__dirname).then(function(files) {
    console.log(files);
}).catch(function(e) {
    console.error(e); {
});

Ответ 20

Для развлечения здесь приведена потоковая версия, которая работает с библиотекой потоков highland.js. Он был соавтором Виктора Ву.

###
  directory >---m------> dirFilesStream >---------o----> out
                |                                 |
                |                                 |
                +--------< returnPipe <-----------+

  legend: (m)erge  (o)bserve

 + directory         has the initial file
 + dirListStream     does a directory listing
 + out               prints out the full path of the file
 + returnPipe        runs stat and filters on directories

###

_ = require('highland')
fs = require('fs')
fsPath = require('path')

directory = _(['someDirectory'])
mergePoint = _()
dirFilesStream = mergePoint.merge().flatMap((parentPath) ->
  _.wrapCallback(fs.readdir)(parentPath).sequence().map (path) ->
    fsPath.join parentPath, path
)
out = dirFilesStream
# Create the return pipe
returnPipe = dirFilesStream.observe().flatFilter((path) ->
  _.wrapCallback(fs.stat)(path).map (v) ->
    v.isDirectory()
)
# Connect up the merge point now that we have all of our streams.
mergePoint.write directory
mergePoint.write returnPipe
mergePoint.end()
# Release backpressure.  This will print files as they are discovered
out.each H.log
# Another way would be to queue them all up and then print them all out at once.
# out.toArray((files)-> console.log(files))

Ответ 21

Используя Promises (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);
    });
  });
};

Он возвращает обещание массива, поэтому вы можете использовать его как:

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

Ответ 22

Я должен добавить библиотеку sander в список.

 var sander = require('sander');
 sander.lsr(directory).then( filenames => { console.log(filenames) } );

Ответ 23

Использование bluebird prom.coroutine:

let promise = require('bluebird'),
    PC = promise.coroutine,
    fs = promise.promisifyAll(require('fs'));
let getFiles = PC(function*(dir){
    let files = [];
    let contents = yield fs.readdirAsync(dir);
    for (let i = 0, l = contents.length; i < l; i ++) {
        //to remove dot(hidden) files on MAC
        if (/^\..*/.test(contents[i])) contents.splice(i, 1);
    }
    for (let i = 0, l = contents.length; i < l; i ++) {
        let content = path.resolve(dir, contents[i]);
        let contentStat = yield fs.statAsync(content);
        if (contentStat && contentStat.isDirectory()) {
            let subFiles = yield getFiles(content);
            files = files.concat(subFiles);
        } else {
            files.push(content);
        }
    }
    return files;
});
//how to use
//easy error handling in one place
getFiles(your_dir).then(console.log).catch(err => console.log(err));

Ответ 24

Потому что каждый должен написать свой собственный, я сделал один.

walk (dir, cb, endCb) центибар (файл) endCb (err | null)

ГРЯЗНА

module.exports = walk;

function walk(dir, cb, endCb) {
  var fs = require('fs');
  var path = require('path');

  fs.readdir(dir, function(err, files) {
    if (err) {
      return endCb(err);
    }

    var pending = files.length;
    if (pending === 0) {
      endCb(null);
    }
    files.forEach(function(file) {
      fs.stat(path.join(dir, file), function(err, stats) {
        if (err) {
          return endCb(err)
        }

        if (stats.isDirectory()) {
          walk(path.join(dir, file), cb, function() {
            pending--;
            if (pending === 0) {
              endCb(null);
            }
          });
        } else {
          cb(path.join(dir, file));
          pending--;
          if (pending === 0) {
            endCb(null);
          }
        }
      })
    });

  });
}

Ответ 25

проверить loaddir https://npmjs.org/package/loaddir

npm install loaddir

  loaddir = require('loaddir')

  allJavascripts = []
  loaddir({
    path: __dirname + '/public/javascripts',
    callback: function(){  allJavascripts.push(this.relativePath + this.baseName); }
  })

Вы можете использовать fileName вместо baseName, если вам нужно расширение.

Дополнительный бонус - это то, что он будет смотреть файлы также и снова вызвать обратный вызов. Существует множество вариантов конфигурации, чтобы сделать его чрезвычайно гибким.

Я просто переделаю камень guard из ruby ​​с помощью loaddir за короткое время

Ответ 26

Это мой ответ. Надеюсь, это может помочь кому-то.

Мой фокус заключается в том, что процедура поиска может останавливаться в любом месте и для найденного файла указывает относительную глубину исходного пути.

var _fs = require('fs');
var _path = require('path');
var _defer = process.nextTick;

// next() will pop the first element from an array and return it, together with
// the recursive depth and the container array of the element. i.e. If the first
// element is an array, it'll be dug into recursively. But if the first element is
// an empty array, it'll be simply popped and ignored.
// e.g. If the original array is [1,[2],3], next() will return [1,0,[[2],3]], and
// the array becomes [[2],3]. If the array is [[[],[1,2],3],4], next() will return
// [1,2,[2]], and the array becomes [[[2],3],4].
// There is an infinity loop `while(true) {...}`, because I optimized the code to
// make it a non-recursive version.
var next = function(c) {
    var a = c;
    var n = 0;
    while (true) {
        if (a.length == 0) return null;
        var x = a[0];
        if (x.constructor == Array) {
            if (x.length > 0) {
                a = x;
                ++n;
            } else {
                a.shift();
                a = c;
                n = 0;
            }
        } else {
            a.shift();
            return [x, n, a];
        }
    }
}

// cb is the callback function, it have four arguments:
//    1) an error object if any exception happens;
//    2) a path name, may be a directory or a file;
//    3) a flag, `true` means directory, and `false` means file;
//    4) a zero-based number indicates the depth relative to the original path.
// cb should return a state value to tell whether the searching routine should
// continue: `true` means it should continue; `false` means it should stop here;
// but for a directory, there is a third state `null`, means it should do not
// dig into the directory and continue searching the next file.
var ls = function(path, cb) {
    // use `_path.resolve()` to correctly handle '.' and '..'.
    var c = [ _path.resolve(path) ];
    var f = function() {
        var p = next(c);
        p && s(p);
    };
    var s = function(p) {
        _fs.stat(p[0], function(err, ss) {
            if (err) {
                // use `_defer()` to turn a recursive call into a non-recursive call.
                cb(err, p[0], null, p[1]) && _defer(f);
            } else if (ss.isDirectory()) {
                var y = cb(null, p[0], true, p[1]);
                if (y) r(p);
                else if (y == null) _defer(f);
            } else {
                cb(null, p[0], false, p[1]) && _defer(f);
            }
        });
    };
    var r = function(p) {
        _fs.readdir(p[0], function(err, files) {
            if (err) {
                cb(err, p[0], true, p[1]) && _defer(f);
            } else {
                // not use `Array.prototype.map()` because we can make each change on site.
                for (var i = 0; i < files.length; i++) {
                    files[i] = _path.join(p[0], files[i]);
                }
                p[2].unshift(files);
                _defer(f);
            }
        });
    }
    _defer(f);
};

var printfile = function(err, file, isdir, n) {
    if (err) {
        console.log('-->   ' + ('[' + n + '] ') + file + ': ' + err);
        return true;
    } else {
        console.log('... ' + ('[' + n + '] ') + (isdir ? 'D' : 'F') + ' ' + file);
        return true;
    }
};

var path = process.argv[2];
ls(path, printfile);

Ответ 27

Здесь рекурсивный метод получения всех файлов, включая подкаталоги.

const FileSystem = require("fs");
const Path = require("path");

//...

function getFiles(directory) {
    directory = Path.normalize(directory);
    let files = FileSystem.readdirSync(directory).map((file) => directory + Path.sep + file);

    files.forEach((file, index) => {
        if (FileSystem.statSync(file).isDirectory()) {
            Array.prototype.splice.apply(files, [index, 1].concat(getFiles(file)));
        }
    });

    return files;
}

Ответ 28

Еще один простой и полезный

function walkDir(root) {
    const stat = fs.statSync(root);

    if (stat.isDirectory()) {
        const dirs = fs.readdirSync(root).filter(item => !item.startsWith('.'));
        let results = dirs.map(sub => walkDir(`${root}/${sub}`));
        return [].concat(...results);
    } else {
        return root;
    }
}

Ответ 29

Вот как я использую функцию nodejs fs.readdir для рекурсивного поиска в каталоге.

const fs = require('fs');
const mime = require('mime-types');
const readdirRecursivePromise = path => {
    return new Promise((resolve, reject) => {
        fs.readdir(path, (err, directoriesPaths) => {
            if (err) {
                reject(err);
            } else {
                if (directoriesPaths.indexOf('.DS_Store') != -1) {
                    directoriesPaths.splice(directoriesPaths.indexOf('.DS_Store'), 1);
                }
                directoriesPaths.forEach((e, i) => {
                    directoriesPaths[i] = statPromise(`${path}/${e}`);
                });
                Promise.all(directoriesPaths).then(out => {
                    resolve(out);
                }).catch(err => {
                    reject(err);
                });
            }
        });
    });
};
const statPromise = path => {
    return new Promise((resolve, reject) => {
        fs.stat(path, (err, stats) => {
            if (err) {
                reject(err);
            } else {
                if (stats.isDirectory()) {
                    readdirRecursivePromise(path).then(out => {
                        resolve(out);
                    }).catch(err => {
                        reject(err);
                    });
                } else if (stats.isFile()) {
                    resolve({
                        'path': path,
                        'type': mime.lookup(path)
                    });
                } else {
                    reject(`Error parsing path: ${path}`);
                }
            }
        });
    });
};
const flatten = (arr, result = []) => {
    for (let i = 0, length = arr.length; i < length; i++) {
        const value = arr[i];
        if (Array.isArray(value)) {
            flatten(value, result);
        } else {
            result.push(value);
        }
    }
    return result;
};

Скажем, у вас есть путь под названием '/database' в вашем корневом проекте node. Как только это обещание будет разрешено, оно должно выплюнуть массив каждого файла в разделе "/database".

readdirRecursivePromise('database').then(out => {
    console.log(flatten(out));
}).catch(err => {
    console.log(err);
});

Ответ 30

Еще один ответ, но на этот раз с использованием 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);
          }
        }
      });
    });
  });
};