node.js fs.readdir özyinelemeli dizin araması


268

Fs.readdir kullanarak bir zaman uyumsuz dizin araması hakkında herhangi bir fikir? Ben özyineleme tanıtmak ve okumak için sonraki dizin ile okuma dizini işlevini çağırabileceğini, ama o zaman async olmaktan endişe duyuyorum ...

Herhangi bir fikir? Harika bir düğüm yürüyüşüne baktım , fakat readdir gibi bir dizideki dosyaları vermiyor. olmasına rağmen

Gibi çıktılar aranıyor ...

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

Yanıtlar:


379

Bunu başarmanın temel olarak iki yolu vardır. Zaman uyumsuz bir ortamda iki tür döngü olduğunu fark edeceksiniz: seri ve paralel. Bir seri döngü, bir sonraki yinelemeye geçmeden önce bir yinelemenin tamamlanmasını bekler - bu, döngünün her yinelemesinin sırayla tamamlanmasını garanti eder. Paralel bir döngüde, tüm yinelemeler aynı anda başlatılır ve biri diğerinden önce tamamlanabilir, ancak seri bir döngüden çok daha hızlıdır. Bu durumda, muhtemelen paralel bir döngü kullanmak daha iyidir, çünkü sonuçları tamamlayıp geri döndürdüğü sürece (sırayla istemediğiniz sürece) yürüyüşün hangi sırayla tamamlandığı önemli değildir.

Paralel bir döngü şöyle görünür:

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

Bir seri döngü şöyle görünür:

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

Ve ana dizininizde test etmek için (UYARI: ana dizininizde çok fazla şey varsa sonuç listesi büyük olacaktır):

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

EDIT: Geliştirilmiş örnekler.


10
Dikkat edin, yukarıdaki chjj "paralel döngü" yanıtı boş bir klasör yürüdü durumunda bir hata vardır. Düzeltme: var beklemede = list.length; eğer (! beklemede) yapılırsa (null, sonuçlar); // bu satırı ekle! list.forEach (function (file) {...
Vasil Daskalopoulos

27
file = dir + '/' + file;Bu önerilmez. Kullanmalısınız: var path = require('path'); file = path.resolve(dir, file);
Leiko

7
@onetrickpony çünkü kullanırsanız path.resolve(...)Windows veya Unix'te uygun bir yol elde edersiniz :) C:\\some\\foo\\pathWindows ve /some/foo/pathUnix sistemlerinde olduğu gibi bir şey elde edeceğiniz anlamına gelir
Leiko

19
2011'de ilk kez yazdığınızda cevabınız harika oldu, ancak 2014'te insanlar açık kaynak modülleri kullanıyor ve daha az kod yazıyor ve kendilerinin ve diğer birçok kişinin bağlı olduğu modüllere katkıda bulunuyorlar. Örneğin , bu kod satırını kullanarak @crawf tarafından istenen çıktıyı elde etmek için node-dir komutunu deneyin:require('node-dir').files(__dirname, function(err, files) { console.log(files); });
Christiaan Westerbeek

5
!--Sözdizimi hakkında karışan herkes için bu konuda bir soru soruldu
Tas

146

Bu, Promise'lar, vaat etme / vaat etme, yıkım, async-await, harita + azaltma ve daha fazlası dahil olmak üzere 8 numaralı düğümde bulunan maksimum yeni, moda sözcük özelliklerini kullanıyor ve iş arkadaşlarınızı neyi anlamaya çalışırken başlarını çiziyor oluyor.

Düğüm 8+

Harici bağımlılık yok.

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

kullanım

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

Düğüm 10.10+

Daha fazla vızıltı ile 10+ düğümü için güncellendi:

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 düğümünden başlayarak files dizisini düzleştirmek files.flat()yerine kullanabileceğinizi unutmayın Array.prototype.concat(...files).

Düğüm 11+

Herkesin kafasını tamamen havaya uçurmak istiyorsanız, async yineleyicileri kullanarak aşağıdaki sürümü kullanabilirsiniz . Gerçekten havalı olmasının yanı sıra, tüketicilerin sonuçları birer birer çıkarmasını sağlayarak, gerçekten büyük dizinler için daha uygun olmasını sağlar.

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

Dönüş türü artık vaat yerine zaman uyumsuz yineleyici olduğu için kullanım değişti

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

Birinin ilgilenmesi durumunda, burada asenkron yineleyiciler hakkında daha fazla yazdım: https://qwtel.com/posts/software/async-generators-in-the-wild/


5
İsimlendirilmesi subdirve subdirsbu aslında dosyalar olabileceğinden, yanıltıcıdır (ı gibi bir şey önermek itemInDirveya item_in_dirhatta sadece item. Bunun yerine), ancak bu çözüm kabul olandan daha temiz hisseder ve daha az kodudur. Ayrıca kabul edilen cevaptaki koddan çok daha karmaşık bulmuyorum. +1
Zelphir Kaltstahl

1
Bunu kullanarak daha da vızıltı yapabilir require(fs).promisesve sadece util.promisifytamamen bırakabilirsiniz . Şahsen ben fs'yi fs.promises ile taklit ediyorum
MushinNoShin

2
Bunu küçük bir değişiklikle daha hızlı yapabiliriz: 2. argümanı readdirAKA'ya seçenekler nesnesi geçirerek, readdir(dir, {withFileTypes: true})bu şekilde tüm bilgileri tür bilgileriyle geri döndürür, bu yüzden şimdi bize veren statbilgileri elde etmek için aramamıza gerek yoktur. readdirgeri. Bu bizi ek sys çağrıları yapmaktan kurtarır. Ayrıntılar burada
cacoder

1
@cacoder Dahil etmek için güncellendi withFileTypes. Bahşiş için teşekkürler.
qwtel

Değiştirmek eğer düğüm 10.10+ içinde, return Array.prototype.concat(...files);birlikte let result = Array.prototype.concat(...files); return result.map(file => file.split('\\').join('/'));kalmasına neden olabilir emin dirs bir "/" değil, bir "\" dönün. return result.map(file => file.replace(/\\/g, '/'));
Normal ifade

106

Herkesin yararlı bulması durumunda, senkronize bir versiyon da bir araya getirdim .

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

İpucu: Filtreleme sırasında daha az kaynak kullanmak için. Bu işlevin içinde filtre uygulayın. Örneğin results.push(file);aşağıdaki kodla değiştirin . Gerektiği gibi ayarlayın:

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

60
Yarı kolonun olmaması dışında bu çözümü seviyorum!
mpen

Bu basit. Ama aynı zamanda biraz saf. Bir dizin bir üst dizine bağlantı içeriyorsa yığın akışına neden olabilir. Belki de lstatyerine? Veya özyineleme düzeyini sınırlamak için özyineleme denetimi ekleyin.
conradkleinespel

14
File =
requir

16
@mpen Yarı-kolonlar gereksiz
Ally

Bu benim için de en iyi sonucu verir. Yine de belirli bir dosya uzantısı için filtre uygulamak için bir filtre ekledim.
Brian

87

A. Dosya modülüne bir göz atın . Walk adlı bir işlevi vardır:

file.walk (başlangıç, geri arama)

Her dosya için geri arama çağırarak (null, dirPath, dirs, files) bir dosya ağacında gezinir.

Bu senin için olabilir! Ve evet, bu zaman uyumsuz. Bununla birlikte, eğer gerekirse, tam yolu kendiniz toplamanız gerekeceğini düşünüyorum.

B. Bir alternatif ve hatta favorilerimden biri: findbunun için unix'i kullanın . Neden zaten programlanmış bir şey yapıyorsunuz? Belki tam olarak ihtiyacınız olan şey değil, ama yine de kontrol etmeye değer:

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, yalnızca birkaç klasör değiştiği sürece sonraki aramaları çok hızlı hale getiren güzel bir yerleşik önbellekleme mekanizmasına sahiptir.


9
Bu sadece UNIX mi?
Mohsen

Örnek B hakkında bir sorunuz vardı: execFile () (ve exec ()) için stderr ve stdout Buffers'tır. Bu nedenle, Buffers Dizeler olmadığı için stdout.toString.split ("\ n") yapmanıza gerek yoktur?
Cheruvim

8
güzel, ama çapraz platform değil.
f0ster

Bu arada: Hayır, A sadece Unix değil! Yalnızca B yalnızca Unix'tir. Ancak, Windows 10 şimdi bir Linux alt sistemi ile geliyor. Bu yüzden B bile bugünlerde sadece Windows üzerinde çalışacaktı.
Johann Philipp Strathausen

WSL'nin Windows üzerinde çalışması için son kullanıcıların PC'sinde etkinleştirilmesi gerekmez mi?
oldboy

38

Başka bir güzel npm paketi glob .

npm install glob

Çok güçlüdür ve tüm yinelenen ihtiyaçlarınızı karşılamalıdır.

Düzenle:

Aslında glob ile mükemmel bir şekilde mutlu değildim, bu yüzden readdirp oluşturdum .

API'sinin dosyaları ve dizinleri tekrar tekrar bulmayı ve belirli filtreleri uygulamayı çok kolay hale getirdiğinden çok eminim.

Ne yaptığı hakkında daha iyi bir fikir edinmek ve aşağıdaki yollarla yüklemek için belgelerini okuyun :

npm install readdirp


Bence en iyi modül . Ve Grunt, Mocha, vb.Gibi diğer birçok proje ve diğer 80.000 + diğer projeler gibi. Sadece söylüyorum.
Yanick Rochon

29

Bu görevi gerçekleştirmek için düğüm glob kullanmanızı tavsiye ederim .

var glob = require( 'glob' );  

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

14

Bir npm paketi kullanmak istiyorsanız, anahtar oldukça iyidir.

var wrench = require("wrench");

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

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

EDIT (2018):
Son zamanlarda okuyan herkes: Yazar 2015 yılında bu paketi kullanımdan kaldırmıştır:

wrench.js kullanımdan kaldırıldı ve bir süredir güncellenmedi. Ekstra dosya sistemi işlemleri yapmak için fs-extra kullanmanızı önemle tavsiye ederim .


@Domenic, bunu nasıl yapıyorsun denodify? Geri arama birkaç kez tetiklenir (yinelemeli olarak). Yani kullanmak Q.denodify(wrench.readdirRecursive)sadece ilk sonucu döndürür.
Onur Yıldırım

1
@ OnurYıldırım evet, bu vaatler için olduğu gibi uygun değil. Birden fazla söz veren bir şey veya bir söz vermeden önce tüm alt dizinler numaralandırılıncaya kadar bekleyen bir şey yazmanız gerekir. İkincisi için, bkz. Github.com/kriskowal/q-io#listdirectorytreepath
Domenic

9

Sevdiğim cevabı gelen chjj üstünde ve o başlangıçtan olmadan paralel döngünün benim sürümünü oluşturmak mümkün olmazdı.

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;

Ben de bir Gist yarattım . Yorumlarınızı bekliyoruz. Hala NodeJS dünyasında başlıyorum, bu yüzden daha fazla öğrenmeyi umuyorum.


9

Özyineleme ile

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

çağrı

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

3
Birlikte yol dizeleri katılmadan değil öneririm /ama kullanarak pathmodülü: path.join(searchPath, file). Bu şekilde, işletim sisteminden bağımsız olarak doğru yollar elde edersiniz.
Moritz Friedrich

8

Tam olarak istediğiniz çıktıyı üretmek için node-dir komutunu kullanın

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 iyi çalışıyordu, ancak webpack ile kullandığımda bazı garip sorunlar yaşıyorum. ReadFiles işlevine, "if (err) Â {" öğesinde olduğu gibi bir "yakalanmadı" yakalanmayan SyntaxError: Beklenmeyen simge {"hatasına neden oluyor. Bu sorundan rahatsız oldum ve hemen tepkim düğüm düğümünü benzer bir şeyle değiştirmektir
Parth

1
@ Parth bu yorum size cevap vermeyecek. SO'ya yeni bir tam soru yazın veya GitHub deposunda bir sorun oluşturun. Sorunuzu iyi detaylandırdığınızda, sorununuzu göndermek zorunda kalmadan bile çözebilirsiniz
Christiaan Westerbeek

1
@ Parth'ın yorumu, önerinizi sorunlarına çözüm olarak düşünen başkaları için hala yararlı bir uyarı olabilir. Bu yorumlar bölümünde cevap

4

Bunu son zamanlarda kodladım ve bunu burada paylaşmanın mantıklı olacağını düşündüm. Kod, zaman uyumsuz kitaplığı kullanır .

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

Bu şekilde kullanabilirsiniz:

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

2
Bu. Bu çok düzenli ve kullanımı kolaydır. Bir modüle pompaladım, gerekli ve mcdream sandviç gibi çalışıyor.
Jay

4

Filehound adında bir kütüphane başka bir seçenektir. Belirli bir dizini özyinelemeli olarak arar (varsayılan olarak çalışma dizini). Çeşitli filtreleri, geri aramaları, vaatleri ve aramaları senkronize etmeyi destekler.

Örneğin, geçerli çalışma dizininde tüm dosyaları arayın (geri çağrıları kullanarak):

const Filehound = require('filehound');

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

Veya belirli bir dizini belirtme ve vaat etme:

const Filehound = require('filehound');

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

Daha fazla kullanım durumu ve kullanım örnekleri için dokümanlara bakın: https://github.com/nspragg/filehound

Feragatname: Ben yazarım.


4

Async / await kullanarak, bunun çalışması gerekir:

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 kullanabilirsiniz.Promisify veya bu:

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

Düğüm 8+, yerleşik Promisify'a sahiptir

Sonuçları daha da hızlı verebilecek bir jeneratör yaklaşımı için diğer cevabımı görün .


4

zaman uyumsuz

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)

Eşitleme

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

Zaman uyumsuz okunabilir

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)

Not: her iki sürüm de sembolik bağlantıları izleyecektir (orijinaliyle aynı fs.readdir)


3

Final-fs kütüphanesine göz atın . Bir readdirRecursiveişlev sağlar:

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

Bağımsız söz uygulaması

Bu örnekte when.js promise kütüphanesini kullanıyorum .

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

includeDirEğer ayarlanırsa dosya listesinde dizinleri içeren isteğe bağlı bir parametre ekledim true.



1

İşte başka bir uygulama. Yukarıdaki çözümlerin hiçbirinde sınırlayıcı yoktur ve bu nedenle dizin yapınız büyükse, hepsi çöker ve sonunda kaynaklar tükenir.

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'lik bir eşzamanlılık kullanmak oldukça iyi çalışıyor ve küçük dizin yapıları için neredeyse daha basit uygulamalar kadar hızlı.



1

Bluebird ile çalışmak için Trevor Senior's Promise tabanlı cevabını değiştirdim

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

Eğlence için, highland.js akış kitaplığıyla çalışan akış tabanlı bir sürüm. Victor Vu tarafından ortak yazılmıştır.

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

Bunu İşlevsel bir tarzda çözmek için Promises ( Q ) kullanma :

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

Bir dizinin vaadini döndürür, böylece diziyi şu şekilde kullanabilirsiniz:

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

1

Promise tabanlı zımpara kütüphanesini listeye eklemeliyim .

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

1

Bluebird sözünü kullanarak.

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

Herkes kendi yazdığı için ben bir tane yaptım.

walk (dir, cb, endCb) cb (dosya) endCb (err | null)

KİRLİ

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'e bakın 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); }
  })

Sen kullanabilirsiniz fileNameyerinebaseName ihtiyacınız varsa .

Ek bir avantaj, dosyaları da izlemesi ve geri aramayı yeniden çağırmasıdır. Son derece esnek hale getirmek için tonlarca yapılandırma seçeneği vardır.

Ben sadece guardkısa bir süre loaddir kullanarak yakut gem yeniden


0

Bu benim cevabım. Umarım birine yardımcı olabilir.

Odak noktam, arama rutininin herhangi bir yerde durmasını sağlamak ve bulunan bir dosya için orijinal yola göreceli derinliği bildirmektir.

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

Alt dizinler dahil tüm dosyaları almanın özyinelemeli bir yöntemi.

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

Bir başka basit ve yardımcı olan

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

Kök dizindeki her dosyanın burada bir klasör olduğunu varsayıyorsunuz.
xechelonx

0

Ben bir dizini özyinelemeli aramak için nodejs fs.readdir işlevini bu şekilde kullanıyorum.

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

Düğüm projeleri kökünde '/ database' adında bir yolunuz olduğunu varsayalım. Bu söz çözüldükten sonra, '/ database' altındaki her dosyanın bir dizisini vermelidir.

readdirRecursivePromise('database').then(out => {
    console.log(flatten(out));
}).catch(err => {
    console.log(err);
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.