node.js fs.readdir рекурсивный поиск в каталоге - PullRequest
229 голосов
/ 29 апреля 2011

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

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

Ищем вывод вроде ...

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

Ответы [ 33 ]

4 голосов
/ 23 февраля 2017

Используя 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

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

4 голосов
/ 08 апреля 2013

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

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);
});
3 голосов
/ 14 ноября 2016

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

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

const Filehound = require('filehound');

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

Или обещания и указание конкретного каталога:

const Filehound = require('filehound');

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

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

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

3 голосов
/ 06 июня 2013

Проверьте библиотеку 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
    });
2 голосов
/ 23 января 2017

klaw и klaw-sync стоит рассмотреть для такого рода вещей.Эти были частью node-fs-extra .

2 голосов
/ 21 сентября 2013

Автономная реализация обещания

Я использую библиотеку обещаний when.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.

1 голос
/ 25 ноября 2015

Использование 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); });
1 голос
/ 10 сентября 2015

Для забавы, это потоковая версия, которая работает с библиотекой потоков 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))
1 голос
/ 25 января 2015

Я изменил ответ Тревора-старшего Обещание для работы с Синяя птица

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); {
});
1 голос
/ 25 ноября 2014

Модуль recursive-readdir обладает этой функциональностью.

...