334

fs.readdirを使用した非同期ディレクトリ検索に関するアイデアはありますか?再帰を導入し、次のディレクトリでディレクトリの読み取り関数を呼び出して読み取ることができることを認識していますが、非同期ではないことが少し心配です...

何か案は?私はノードウォークを見てきましたが、これは素晴らしいですが、readdirのように配列内のファイルだけを提供するわけではありません。それでも

次のような出力を探しています...

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

42 に答える 42

422

これを実現するには、基本的に2つの方法があります。非同期環境では、シリアルとパラレルの2種類のループがあることに気付くでしょう。シリアルループは、1つの反復が完了するのを待ってから、次の反復に移動します。これにより、ループのすべての反復が順番に完了することが保証されます。並列ループでは、すべての反復が同時に開始され、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);
});

編集:改善された例。

于 2011-04-29T04:29:35.503 に答える
287

これは、ノード8で利用可能な新しい流行語機能を最大限に使用します。これには、Promises、util / promise、destructuring、async-await、map + reduceなどが含まれ、同僚が何を理解しようとして頭を悩ませます。起こっています。

ノード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), []);
}

使用法

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

ノード10.10+

ノード10+用に更新され、さらに多くのwhizbangが追加されました。

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

async function getFiles(dir) {
  const dirents = await readdir(dir, { withFileTypes: true });
  const files = await Promise.all(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+

みんなの頭を完全に爆破したい場合は、非同期イテレータを使用して次のバージョンを使用できます。非常に優れているだけでなく、消費者は一度に1つずつ結果を取得できるため、非常に大きなディレクトリに適しています。

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

returnタイプがpromiseではなく非同期イテレータになっているため、使用法が変更されました

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

誰かが興味を持っている場合に備えて、非同期イテレータについて詳しくは、https ://qwtel.com/posts/software/async-generators-in-the-wild/を参照してください。

于 2017-07-16T16:42:04.073 に答える
131

誰かがそれが役に立つと思う場合に備えて、私は同期バージョンもまとめました。

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);
于 2013-05-22T06:02:42.740 に答える
91

A.ファイルモジュールを見てください。ウォークと呼ばれる機能があります。

file.walk(start、callback)

ファイルツリーをナビゲートし、各ディレクトリのコールバックを呼び出して、(null、dirPath、dirs、files)を渡します。

これはあなたのためかもしれません!そして、はい、それは非同期です。ただし、必要に応じて、フルパスを自分で集約する必要があると思います。

B.別の方法、そして私のお気に入りの1つでさえ、そのためにUNIXfindを使用します。すでにプログラムされている何かをもう一度行うのはなぜですか?必要なものとは異なるかもしれませんが、それでもチェックする価値があります。

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には、変更されたフォルダが少ない限り、後続の検索を非常に高速にする優れた組み込みキャッシュメカニズムがあります。

于 2011-06-15T13:44:11.507 に答える
45

もう1つの素晴らしいnpmパッケージはglobです。

npm install glob

これは非常に強力であり、繰り返し発生するすべてのニーズをカバーする必要があります。

編集:

私は実際にはglobに完全に満足していなかったので、readdirpを作成しました。

そのAPIにより、ファイルとディレクトリを再帰的に検索し、特定のフィルターを非常に簡単に適用できると確信しています。

そのドキュメントを読んで、それが何をするのかをよりよく理解し、次の方法でインストールしてください。

npm install readdirp

于 2012-06-01T12:25:57.543 に答える
44

そのタスクを実行するには、node-globを使用することをお勧めします。

var glob = require( 'glob' );  

glob( 'dirname/**/*.js', function( err, files ) {
  console.log( files );
});
于 2015-03-10T23:58:19.007 に答える
17

npmパッケージを使用したい場合は、レンチが適しています。

var wrench = require("wrench");

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

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

編集(2018):
最近読んだ人:著者は2015年にこのパッケージを非推奨にしました:

wrench.jsは非推奨であり、かなり長い間更新されていません。追加のファイルシステム操作を行うには、fs-extraを使用することを強くお勧めします。

于 2012-04-24T17:45:03.297 に答える
10

私は上記の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;

要旨も作成しました。コメントを歓迎します。私はまだNodeJSの領域から始めているので、それが私がもっと学びたいと思っている1つの方法です。

于 2012-09-14T00:16:23.013 に答える
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
于 2016-04-19T22:54:05.020 に答える
9

非同期

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

const readdir = (p, done, a = [], i = 0) => fs.readdir(p, (e, d = []) =>
  d.map(f => readdir(a[a.push(path.join(p, f)) - 1], () =>
    ++i == d.length && done(a), a)).length || done(a))

readdir(__dirname, console.log)

同期

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

const readdirSync = (p, a = []) => {
  if (fs.statSync(p).isDirectory())
    fs.readdirSync(p).map(f => readdirSync(a[a.push(path.join(p, f)) - 1], a))
  return a
}

console.log(readdirSync(__dirname))

非同期読み取り可能

function readdir (currentPath, done, allFiles = [], i = 0) {
  fs.readdir(currentPath, function (e, directoryFiles = []) {
    if (!directoryFiles.length)
      return done(allFiles)
    directoryFiles.map(function (file) {
      var joinedPath = path.join(currentPath, file)
      allFiles.push(joinedPath)
      readdir(joinedPath, function () {
        i = i + 1
        if (i == directoryFiles.length)
          done(allFiles)}
      , allFiles)
    })
  })
}

readdir(__dirname, console.log)

注:どちらのバージョンもシンボリックリンクをたどります(元のバージョンと同じfs.readdir

于 2019-04-08T04:04:24.980 に答える
8

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);
  })
});
于 2014-05-14T14:46:34.900 に答える
6

最新のpromiseベースのreaddir再帰バージョン:

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

const readDirRecursive = async (filePath) => {
    const dir = await fs.promises.readdir(filePath);
    const files = await Promise.all(dir.map(async relativePath => {
        const absolutePath = path.join(filePath, relativePath);
        const stat = await fs.promises.lstat(absolutePath);

        return stat.isDirectory() ? readDirRecursive(absolutePath) : absolutePath;
    }));

    return files.flat();
}
于 2021-09-24T17:00:45.393 に答える
4

私は最近これをコーディングしましたが、ここでこれを共有するのは理にかなっていると思いました。コードは非同期ライブラリを利用します。

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);
});
于 2013-04-08T15:15:29.450 に答える
4

Filehoundと呼ばれるライブラリは別のオプションです。指定されたディレクトリ(デフォルトでは作業ディレクトリ)を再帰的に検索します。さまざまなフィルター、コールバック、Promise、同期検索をサポートしています。

たとえば、現在の作業ディレクトリですべてのファイルを検索します(コールバックを使用)。

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

免責事項:私は著者です。

于 2016-11-14T19:28:38.810 に答える
4

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

Node8 +にはPromisifyが組み込まれています

結果をさらに速くすることができるジェネレーターアプローチについては、私の他の回答を参照してください。

于 2017-02-23T00:39:58.327 に答える
4

これが単純な同期再帰ソリューションです

const fs = require('fs')

const getFiles = path => {
    const files = []
    for (const file of fs.readdirSync(path)) {
        const fullPath = path + '/' + file
        if(fs.lstatSync(fullPath).isDirectory())
            getFiles(fullPath).forEach(x => files.push(file + '/' + x))
        else files.push(file)
    }
    return files
}

使用法:

const files = getFiles(process.cwd())

console.log(files)

非同期で書くこともできますが、必要はありません。入力ディレクトリが存在し、アクセス可能であることを確認してください。

于 2021-01-28T13:57:09.270 に答える
3

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
    });
于 2013-06-05T23:37:03.117 に答える
3

TypeScriptでのqwtel回答バリアント

import { resolve } from 'path';
import { readdir } from 'fs/promises';

async function* getFiles(dir: string): AsyncGenerator<string> {
    const entries = await readdir(dir, { withFileTypes: true });
    for (const entry of entries) {
        const res = resolve(dir, entry.name);
        if (entry.isDirectory()) {
            yield* getFiles(res);
        } else {
            yield res;
        }
    }
}
于 2020-12-22T20:03:33.847 に答える
3

シンプルで非同期のPromiseベース


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

于 2021-02-17T08:19:18.963 に答える
2

スタンドアロンのpromiseの実装

この例では、 when.jspromiseライブラリを使用しています。

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

于 2013-09-20T21:32:42.713 に答える
2

klawklaw-syncは、この種のことを検討する価値があります。これらはnode-fs-extraの一部でした。

于 2017-01-23T15:40:53.363 に答える
1

これがさらに別の実装です。上記のソリューションにはリミッターがないため、ディレクトリ構造が大きい場合、それらはすべてスラッシュになり、最終的にはリソースが不足します。

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

50の同時実行性を使用すると、非常にうまく機能し、小さなディレクトリ構造の単純な実装とほぼ同じくらい高速になります。

于 2014-08-06T16:07:04.837 に答える
1

recursive-readdirモジュールにはこの機能があります。

于 2014-11-25T00:19:39.050 に答える
1

Bluebirdで動作するように、TrevorSeniorのPromiseベースの回答を変更しました

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); {
});
于 2015-01-24T21:51:34.880 に答える
1

楽しみのために、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))
于 2015-09-10T16:08:24.150 に答える
1

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

配列のpromiseを返すため、次のように使用できます。

walk('/home/mypath').then(function (files) { console.log(files); });
于 2015-11-25T13:18:17.793 に答える
1

Promiseベースのサンダーライブラリをリストに追加する必要があります。

 var sander = require('sander');
 sander.lsr(directory).then( filenames => { console.log(filenames) } );
于 2016-01-14T12:58:26.113 に答える
1

bluebirdpromise.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));
于 2016-08-24T08:34:08.687 に答える
1

さらに別の答えですが、今回は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);
          }
        }
      });
    });
  });
};

于 2018-05-15T08:20:24.267 に答える
1

ネストされたリターンを処理するためにArray.flat()を使用するTypeScriptのPromiseベースの再帰的ソリューション。

import { resolve } from 'path'
import { Dirent } from 'fs'
import * as fs from 'fs'

function getFiles(root: string): Promise<string[]> {
 return fs.promises
   .readdir(root, { withFileTypes: true })
   .then(dirents => {
      const mapToPath = (r: string) => (dirent: Dirent): string => resolve(r, dirent.name)
      const directoryPaths = dirents.filter(a => a.isDirectory()).map(mapToPath(root))
      const filePaths = dirents.filter(a => a.isFile()).map(mapToPath(root))

     return Promise.all<string>([
       ...directoryPaths.map(a => getFiles(a, include)).flat(),
       ...filePaths.map(a => Promise.resolve(a))
     ]).then(a => a.flat())
  })
}
于 2020-03-09T06:30:34.057 に答える
1

受け入れられた答えの同期的な代替案を望む人は誰でも(私がやったことを知っています):

var fs = require('fs');
var path = require('path');
var walk = function(dir) {
    let results = [], err = null, list;
    try {
        list = fs.readdirSync(dir)
    } catch(e) {
        err = e.toString();
    }
    if (err) return err;
    var i = 0;
    return (function next() {
        var file = list[i++];

        if(!file) return results;
        file = path.resolve(dir, file);
        let stat = fs.statSync(file);
        if (stat && stat.isDirectory()) {
          let res = walk(file);
          results = results.concat(res);
          return next();
        } else {
          results.push(file);
           return next();
        }

    })();

};

console.log(
    walk("./")
)
于 2020-03-31T08:49:03.380 に答える
1

ディレクトリを非常に高速に再帰的に検索するcup-readdirと呼ばれる新しいモジュールがあります。非同期のpromiseを使用し、深いディレクトリ構造を処理するときに多くの一般的なモジュールよりも優れています。

配列内のすべてのファイルを返し、それらをプロパティで並べ替えることができますが、ファイルのフィルタリングや対称化されたディレクトリへの入力などの機能はありません。これは、ディレクトリからすべてのファイルを取得したいだけの大規模なプロジェクトに役立ちます。こちらが彼らのプロジェクトホームページへのリンクです。

于 2021-01-19T23:41:15.080 に答える
1

短くて効率的:

import {lstat, readdir} from 'node:fs/promises'
import {join} from 'node:path/posix'

const deepReadDir = async (dirPath) => await Promise.all(
  (await readdir(dirPath)).map(async (entity) => {
    const path = join(dirPath, entity)
    return (await lstat(path)).isDirectory() ? await deepReadDir(path) : path
  }),
)

これにより、ネストされた各パスが新しい(ネストされた)配列に自動的に折りたたまれます。たとえば、次の場合です。

const files = await deepReadDir('src')

files次のようになります:

[
  [
    'src/client/api.js',
    'src/client/http-constants.js',
    'src/client/index.html',
    'src/client/index.js',
    [ 'src/client/res/favicon.ico' ],
    'src/client/storage.js'
  ],
  [ 'src/crypto/keygen.js' ],
  'src/discover.js',
  [
    'src/mutations/createNewMutation.js',
    'src/mutations/newAccount.js',
    'src/mutations/transferCredit.js',
    'src/mutations/updateApp.js'
  ],
  [
    'src/server/authentication.js',
    'src/server/handlers.js',
    'src/server/quick-response.js',
    'src/server/server.js',
    'src/server/static-resources.js'
  ],
  [ 'src/util/prompt.js', 'src/util/safeWriteFile.js' ],
  'src/util.js'
]

しかし、あなたはそれを簡単に平らにすることができます:

files.flat(Number.POSITIVE_INFINITY)
[
  'src/client/api.js',
  'src/client/http-constants.js',
  'src/client/index.html',
  'src/client/index.js',
  'src/client/res/favicon.ico',
  'src/client/storage.js',
  'src/crypto/keygen.js',
  'src/discover.js',
  'src/mutations/createNewMutation.js',
  'src/mutations/newAccount.js',
  'src/mutations/transferCredit.js',
  'src/mutations/updateApp.js',
  'src/server/authentication.js',
  'src/server/handlers.js',
  'src/server/quick-response.js',
  'src/server/server.js',
  'src/server/static-resources.js',
  'src/util/prompt.js',
  'src/util/safeWriteFile.js',
  'src/util.js'
]
于 2022-02-17T22:24:00.820 に答える
0

誰もが自分で書くべきなので、私が作りました。

walk(dir、cb、endCb)cb(file)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);
          }
        }
      })
    });

  });
}
于 2013-03-05T16:11:22.233 に答える
0

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しばらくの間、loaddirを使用してルビーから宝石を作り直しました

于 2013-08-15T14:31:27.707 に答える
0

これが私の答えです。それが誰かを助けることができることを願っています。

私の焦点は、検索ルーチンをどこでも停止できるようにすることです。見つかったファイルについては、元のパスに対する相対的な深さを示します。

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);
于 2014-09-15T00:38:34.237 に答える
0

サブディレクトリを含むすべてのファイルを再帰的に取得する方法は次のとおりです。

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;
}
于 2017-02-03T04:51:59.290 に答える
0

もう1つのシンプルで役立つもの

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;
    }
}
于 2017-02-24T14:58:40.430 に答える
0

これは、nodejsfs.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」というパスがあるとします。この約束が解決されると、「/database」の下にあるすべてのファイルの配列を吐き出す必要があります。

readdirRecursivePromise('database').then(out => {
    console.log(flatten(out));
}).catch(err => {
    console.log(err);
});
于 2017-05-03T08:13:47.620 に答える
0

ノード10.3以降の場合、待機中のソリューションは次のとおりです

#!/usr/bin/env node

const FS = require('fs');
const Util = require('util');
const readDir = Util.promisify(FS.readdir);
const Path = require('path');

async function* readDirR(path) {
    const entries = await readDir(path,{withFileTypes:true});
    for(let entry of entries) {
        const fullPath = Path.join(path,entry.name);
        if(entry.isDirectory()) {
            yield* readDirR(fullPath);
        } else {
            yield fullPath;
        }
    }
}

async function main() {
    const start = process.hrtime.bigint();
    for await(const file of readDirR('/mnt/home/media/Unsorted')) {
        console.log(file);
    }
    console.log((process.hrtime.bigint()-start)/1000000n);
}

main().catch(err => {
    console.error(err);
});

このソリューションの利点は、結果の処理をすぐに開始できることです。たとえば、メディアディレクトリ内のすべてのファイルを読み取るのに12秒かかりますが、この方法で読み取ると、数ミリ秒以内に最初の結果を取得できます。

于 2019-03-04T07:39:18.493 に答える
0

ただ歩く

let pending = [baseFolderPath]
function walk () {
    pending.shift();
    // do stuffs width pending[0] and change pending items
    if (pending[0]) walk(pending[0])
}
walk(pending[0])
于 2020-04-03T06:18:54.807 に答える
-1

これが完全に機能するコードです。あなたの要件に従って。すべてのファイルとフォルダーを再帰的に取得できます。

var recur = function(dir) {
            fs.readdir(dir,function(err,list){
                list.forEach(function(file){
                    var file2 = path.resolve(dir, file);
                    fs.stat(file2,function(err,stats){
                        if(stats.isDirectory()) {
                            recur(file2);
                        }
                        else {
                            console.log(file2);
                        }
                    })
                })
            });
        };
        recur(path);

パスに「c:\test」のように検索するディレクトリパスを指定します

于 2016-07-09T05:58:05.137 に答える