node.js fs.readdir 재귀 디렉토리 검색


268

fs.readdir을 사용하여 비동기 디렉토리 검색에 대한 아이디어가 있습니까? 재귀를 도입하고 읽을 다음 디렉토리로 디렉토리 읽기 기능을 호출 할 수 있음을 알고 있지만 비동기 적이 지 않을까 걱정됩니다.

어떤 아이디어? 나는 node-walk 를 살펴 보았지만 readdir처럼 배열의 파일 만 제공하지는 않습니다. 이기는 하지만

다음과 같은 출력을 찾고 있습니다 ...

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

답변:


379

이것을 달성하는 기본적으로 두 가지 방법이 있습니다. 비동기 환경에서는 직렬 및 병렬의 두 가지 루프가 있음을 알 수 있습니다. 직렬 루프는 다음 반복으로 넘어 가기 전에 하나의 반복이 완료 될 때까지 기다립니다. 이렇게하면 루프의 모든 반복이 순서대로 완료됩니다. 병렬 루프에서는 모든 반복이 동시에 시작되고 하나가 다른 것보다 먼저 완료 될 수 있지만 직렬 루프보다 훨씬 빠릅니다. 따라서이 경우 결과를 완료하고 반환하는 한 (순서를 원하지 않는 한) 워크가 완료되는 순서가 중요하지 않기 때문에 병렬 루프를 사용하는 것이 좋습니다.

병렬 루프는 다음과 같습니다.

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

편집 : 개선 된 예.


10
위의 chjj에서 "병렬 루프"응답은 빈 폴더를 걸을 경우 버그가 있습니다. 수정 사항은 다음과 같습니다. var pending = list.length; if (! pending) 완료 (null, 결과); //이 줄을 추가하십시오! list.forEach (function (file) {...
Vasil Daskalopoulos

27
file = dir + '/' + file;권장하지 않습니다. 당신은 사용해야합니다 : var path = require('path'); file = path.resolve(dir, file);
Leiko

7
@onetrickpony 사용하는 경우 path.resolve(...)Windows 또는 Unix에 관계없이 올바른 경로를 얻을 수 있기 때문에 :) C:\\some\\foo\\pathWindows 및 /some/foo/pathUnix 시스템 과 같은 것을 얻을 수 있음을 의미
Leiko

19
2011 년에 처음 썼을 때 귀하의 답변이 훌륭했기 때문에 하향 투표를했지만 2014 년에는 오픈 소스 모듈을 사용하고 더 적은 코드를 직접 작성하여 자신과 다른 많은 사람들이 의존하는 모듈에 기여합니다. 예를 들어 다음 코드 줄을 사용하여 @crawf에 필요한 출력을 정확히 얻으려면 node-dir 을 시도 하십시오 .require('node-dir').files(__dirname, function(err, files) { console.log(files); });
Christiaan Westerbeek

5
!--문법 에 대해 혼란스러워하는 사람이라면 누구나 질문에 대해 질문을 받았습니다
Tas

145

이것은 노드 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), []);
}

용법

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

노드 10.10+

더 많은 whizbang과 함께 노드 10 이상에 대해 업데이트되었습니다.

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 이상

모든 사람의 머리를 완전히 날려 버리려면 async iterators 를 사용하여 다음 버전을 사용할 수 있습니다 . 소비자는 정말 시원 할뿐만 아니라 한 번에 하나씩 결과를 가져 와서 실제로 큰 디렉토리에 더 적합하게 만들 수 있습니다.

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
의 이름 subdirsubdirs그 실제 파일이 될 수 있으므로, 오해의 소지가있다 (내가 좋아하는 뭔가를 제안 itemInDir하거나 item_in_dir또는 단순히 item. 대신)을하지만,이 솔루션은 허용 하나보다 깨끗한 느낌이 훨씬 덜 코드입니다. 또한 허용 된 답변의 코드보다 훨씬 복잡하지 않습니다. +1
Zelphir Kaltstahl

1
사용 require(fs).promises하고 util.promisify완전히 떨어 뜨림 으로써 더 많은 위즈 뱅크를 만들 수 있습니다. 개인적으로 fs를 fs.promises로 별칭 지정합니다.
MushinNoShin

2
한 번의 작은 변경으로이를 더 빠르게 만들 수 있습니다. 두 번째 인수를 readdirAKA에 옵션 객체와 같이 전달하면 readdir(dir, {withFileTypes: true})유형 정보가있는 모든 항목이 반환 되므로 이제 우리에게 제공 stat하는 정보를 얻기 위해 전혀 전화 할 필요가 없습니다. readdir뒤. 이를 통해 추가 sys 호출을 할 필요가 없습니다. 자세한 내용은 여기
cacoder

1
@cacoder를 포함하도록 업데이트되었습니다 withFileTypes. 팁 고마워.
qwtel 2016 년

노드 10.10+에서 대체 return Array.prototype.concat(...files);하는 경우 let result = Array.prototype.concat(...files); return result.map(file => file.split('\\').join('/'));dirs가 "\"가 아닌 "/"를 반환하는지 확인할 수 있습니다. 당신이 정규식 괜찮다면, 당신은 또한 할 수return result.map(file => file.replace(/\\/g, '/'));
SwiftNinjaPro

106

누군가가 유용하다고 생각하는 경우 동기 버전을 작성했습니다.

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

60
나는 세미콜론이 없다는 것을 제외 하고는이 솔루션을 좋아합니다!
mpen

이것은 간단합니다. 또한 약간 순진합니다. 디렉토리에 상위 디렉토리에 대한 링크가 포함 된 경우 스택 오버 플로우가 발생할 수 있습니다. 어쩌면 lstat대신 사용 하시겠습니까? 또는 재귀 수준을 제한하기 위해 재귀 검사를 추가하십시오.
conradkleinespel

14
file = require ( "path"). join (dir, file) 사용을 고려하십시오
mkamioner

16
@mpen 세미콜론은 중복됩니다
Ally

이것은 또한 나에게 가장 효과적입니다. 특정 파일 확장자를 필터링하는 필터도 추가했지만.
Brian

87

A. 파일 모듈을 살펴보십시오 . walk라는 기능이 있습니다.

file.walk (시작, 콜백)

(null, dirPath, dirs, files)를 전달하여 각 디렉토리에 대한 콜백을 호출하여 파일 트리를 탐색합니다.

이것은 당신을위한 것일 수 있습니다! 그리고 네, 그것은 비동기입니다. 그러나 필요한 경우 전체 경로를 직접 집계해야한다고 생각합니다.

B. 대안, 심지어 내가 좋아하는 것 중 하나 : 유닉스 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에는 폴더가 거의 변경되지 않는 한 후속 검색을 매우 빠르게 수행하는 멋진 내장 캐싱 메커니즘이 있습니다.


9
이 유닉스 만입니까?
Mohsen

예제 B에 대한 질문이 있습니다. execFile () (및 exec ())의 경우 stderr 및 stdout은 버퍼입니다 .. 버퍼가 문자열이 아니기 때문에 stdout.toString.split ( "\ n")을 수행 할 필요가 없습니까?
Cheruvim

8
훌륭하지만 교차 플랫폼은 아닙니다.
f0ster

그건 그렇고 : 아니오, A는 유닉스만이 아닙니다! B만이 유닉스 전용입니다. 그러나 이제 Windows 10에는 Linux 하위 시스템이 제공됩니다. 따라서 B조차도 요즘 Windows에서 작동합니다.
요한 필립 스트라 트 하우젠

Windows에서 작동하려면 최종 사용자 PC에서 WSL을 사용하지 않아야합니까 ??
oldboy

38

또 다른 멋진 npm 패키지는 glob 입니다.

npm install glob

매우 강력하며 모든 되풀이 요구를 충족해야합니다.

편집하다:

나는 실제로 glob에 완전히 만족하지 않았으므로 readdirp을 만들었습니다 .

API가 파일과 디렉토리를 재귀 적으로 찾고 특정 필터를 쉽게 적용 할 수 있다고 확신합니다.

설명서 를 읽고 내용을 보다 잘 이해하고 다음을 통해 설치하십시오.

npm install readdirp


내 의견으로는 최고의 모듈 . 그리고 그런트, 모카 등과 같은 다른 많은 프로젝트들과 80,000 개 이상의 다른 프로젝트들도 마찬가지입니다. 그냥 말하면
Yanick Rochon

29

해당 작업을 수행하기 위해 node-glob 을 사용하는 것이 좋습니다 .

var glob = require( 'glob' );  

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

14

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사용하는 것이 좋습니다 .


@Domenic, 어떻게 지내 denodify? 콜백이 여러 번 발생합니다 (재귀 적으로). 따라서 using Q.denodify(wrench.readdirRecursive)은 첫 번째 결과 만 반환합니다.
Onur Yıldırım

1
@ OnurYıldırım 예, 이것은 약속에 적합하지 않습니다. 다중 약속을 리턴하는 항목 또는 약속을 리턴하기 전에 모든 서브 디렉토리가 열거 될 때까지 대기하는 항목을 작성해야합니다. 후자는 github.com/kriskowal/q-io#listdirectorytreepath
Domenic

9

위의 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 영역에서 시작하여 더 배우고 싶은 한 가지 방법입니다.


9

재귀

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

3
경로 문자열을 조인하지 않고 모듈을 /사용하는 것이 좋습니다 . 그렇게하면 OS에 관계없이 올바른 경로를 얻을 수 있습니다. pathpath.join(searchPath, file)
Moritz Friedrich

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

node-dir은 정상적으로 작동했지만 webpack과 함께 사용하면 이상한 문제가 있습니다. "if (err) Â {"와 같이 readFiles 함수에 Â가 삽입되어 "포착되지 않은 SyntaxError : Unexpected token {"오류가 발생합니다. 나는이 문제에 시달리고 즉각적인 반응은 node-dir을 비슷한 것으로 바꾸는 것이다
Parth

1
@Parth이 의견은 답변을 드리지 않습니다. SO에 대한 새로운 전체 질문을 작성하거나 GitHub 리포지토리에서 문제를 생성하십시오. 질문에 대해 자세히 설명하면 게시하지 않아도 문제를 해결할 수도 있습니다.
Christiaan Westerbeek

1
@Parth의 의견은 귀하의 제안을 문제의 해결책으로 고려하는 다른 사람들에게 여전히 유용한 경고 일 수 있습니다. 그들은이 코멘트 섹션에서 답을 찾지 못했을 수도 있습니다 :)

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

2
이. 이것은 매우 깔끔하고 사용하기 쉽습니다. 나는 그것을 모듈에 펌핑하고, 그것을 요구했으며 그것은 mcdream 샌드위치처럼 작동합니다.
Jay

4

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

면책 조항 : 나는 저자입니다.


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 를 사용할 수 있습니다 .

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

Node 8+는 기본 제공 약속

더 빠른 결과를 얻을 수있는 생성기 접근 방법에 대한 다른 답변 을 참조하십시오 .


4

비동기

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).


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

2

독립형 약속 구현

이 예제 에서는 when.js promise 라이브러리를 사용하고 있습니다.

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

여기 또 다른 구현이 있습니다. 위의 솔루션에는 제한이 없으므로 디렉토리 구조가 크면 모두 튀어 나와 결국 리소스가 부족합니다.

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을 사용하면 꽤 잘 작동하며 작은 디렉토리 구조에 대한 간단한 구현만큼이나 빠릅니다.



1

Bluebird 와 함께 작동 하도록 Trevor Senior 's 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); {
});

1

재미를 위해 다음은 highland.js 스트림 라이브러리와 함께 작동하는 흐름 기반 버전입니다. Victor Vu가 공동 저술했습니다.

###
  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

약속 ( 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

Promise 기반 샌더 라이브러리를 목록에 추가해야 합니다.

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

1

bluebird promise.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));

0

모두 자신의 글을 써야했기 때문에 하나를 만들었습니다.

walk (dir, cb, endCb) cb (파일) 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);
          }
        }
      })
    });

  });
}

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을 사용하여 루비 에서 보석을 다시 만들었습니다.


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

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

0

또 다른 간단하고 도움이되는 것

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

루트 디렉토리의 모든 파일이 여기 폴더라고 가정합니다.
xechelonx

0

이것이 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'라는 경로가 있다고 가정 해 봅시다. 이 약속이 해결되면 '/ database'아래에있는 모든 파일의 배열을 뱉어야합니다.

readdirRecursivePromise('database').then(out => {
    console.log(flatten(out));
}).catch(err => {
    console.log(err);
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.