node.js fs.readdir pencarian direktori rekursif

268

Adakah gagasan tentang pencarian direktori async menggunakan fs.readdir? Saya menyadari bahwa kita dapat memperkenalkan rekursi dan memanggil fungsi direktori baca dengan direktori berikutnya untuk membaca, tetapi saya sedikit khawatir tentang hal itu tidak menjadi async ...

Ada ide? Saya telah melihat node-walk yang bagus, tetapi tidak memberi saya hanya file dalam array, seperti readdir. Meskipun

Mencari output seperti ...

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

Jawaban:

379

Pada dasarnya ada dua cara untuk mencapai ini. Dalam lingkungan async Anda akan melihat bahwa ada dua jenis loop: serial dan paralel. Serial loop menunggu satu iterasi untuk diselesaikan sebelum berpindah ke iterasi berikutnya - ini menjamin bahwa setiap iterasi loop selesai secara berurutan. Dalam loop paralel, semua iterasi dimulai pada waktu yang sama, dan satu dapat menyelesaikan sebelum yang lain, namun, itu jauh lebih cepat daripada loop serial. Jadi dalam hal ini, mungkin lebih baik menggunakan loop paralel karena tidak masalah urutan jalannya selesai, asalkan selesai dan mengembalikan hasilnya (kecuali jika Anda menginginkannya secara berurutan).

Loop paralel akan terlihat seperti ini:

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

Serial loop akan terlihat seperti ini:

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

Dan untuk mengujinya di direktori home Anda (PERINGATAN: daftar hasil akan sangat besar jika Anda memiliki banyak hal di direktori home Anda):

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

EDIT: Contoh yang ditingkatkan.

chjj
sumber
10
Hati-hati, jawaban "paralel loop" dari chjj di atas memiliki bug dalam kasus ketika folder kosong berjalan. Cara mengatasinya adalah: var pending = list.length; jika (! tertunda) selesai (null, hasil); // tambahkan baris ini! list.forEach (fungsi (file) {...
Vasil Daskalopoulos
27
file = dir + '/' + file;Ini tidak disarankan. Anda harus menggunakan: var path = require('path'); file = path.resolve(dir, file);
Leiko
7
@onetrickpony karena jika Anda menggunakan path.resolve(...)Anda akan mendapatkan jalur yang tepat apakah Anda berada di Windows atau Unix :) Berarti Anda akan mendapatkan sesuatu seperti C:\\some\\foo\\pathdi Windows dan /some/foo/pathpada sistem Unix
Leiko
19
Saya downvoted karena jawaban Anda sangat bagus ketika Anda pertama kali menulis kembali pada tahun 2011, tetapi pada tahun 2014 orang menggunakan modul open source dan menulis lebih sedikit kode sendiri dan berkontribusi pada modul yang mereka dan orang lain bergantung padanya. Sebagai contoh, coba node-dir untuk mendapatkan output yang dibutuhkan oleh @crawf persis menggunakan baris kode ini:require('node-dir').files(__dirname, function(err, files) { console.log(files); });
Christiaan Westerbeek
5
Bagi siapa pun yang bingung tentang !--sintaksis, sebuah pertanyaan telah diajukan tentang hal itu
Tas
147

Yang ini menggunakan jumlah maksimum baru, fitur buzzwordy yang tersedia di node 8, termasuk Janji, util / promisifikasi, perusakan, menunggu async, peta + kurangi dan banyak lagi, membuat rekan kerja Anda menggaruk-garuk kepala saat mereka mencoba mencari tahu apa sedang terjadi.

Node 8+

Tidak ada ketergantungan eksternal.

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

Pemakaian

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

Node 10.10+

Diperbarui untuk node 10+ dengan whizbang lebih banyak:

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

Perhatikan bahwa mulai dengan simpul 11.15.0 Anda dapat menggunakan files.flat()alih-alih Array.prototype.concat(...files)untuk meratakan array file.

Node 11+

Jika Anda ingin meledakkan kepala semua orang sepenuhnya, Anda dapat menggunakan versi berikut menggunakan iterator async . Selain menjadi sangat keren, ini juga memungkinkan konsumen untuk menarik hasil satu per satu, sehingga lebih cocok untuk direktori yang sangat besar.

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

Penggunaan telah berubah karena tipe pengembalian sekarang merupakan iterator async bukan janji

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

Jika seseorang tertarik, saya telah menulis lebih banyak tentang iterator async di sini: https://qwtel.com/posts/software/async-generators-in-the-wild/

qwtel
sumber
5
Penamaan subdirdan subdirsmenyesatkan, karena mereka mungkin benar-benar file (saya sarankan sesuatu seperti itemInDiratau item_in_diratau bahkan hanya itemsebagai gantinya.), Tetapi solusi ini terasa lebih bersih daripada yang diterima dan kode jauh lebih sedikit. Saya juga tidak merasa jauh lebih rumit daripada kode dalam jawaban yang diterima. +1
Zelphir Kaltstahl
1
Anda bisa membuat whizbang ini lebih banyak lagi dengan menggunakan require(fs).promisesdan hanya menjatuhkan util.promisifysepenuhnya. Secara pribadi saya alias fs ke fs.promises.
MushinNoShin
2
Kita dapat membuat ini lebih cepat dengan satu perubahan kecil: meneruskan argumen 2 ke readdirAKA objek pilihan seperti jadi readdir(dir, {withFileTypes: true})ini akan mengembalikan semua item dengan informasi tipenya, jadi kita tidak perlu menelepon statsama sekali untuk mendapatkan informasi yang readdirsekarang memberi kita kembali. Ini menyelamatkan kita dari keharusan membuat panggilan sistem tambahan. Detail di sini
cacoder
1
@cacoder Diperbarui untuk disertakan withFileTypes. Terima kasih atas tipnya.
qwtel
di node 10.10+, jika Anda mengganti return Array.prototype.concat(...files);dengan let result = Array.prototype.concat(...files); return result.map(file => file.split('\\').join('/'));Anda dapat memastikan dirs mengembalikan "/" dan bukan "\". Jika Anda tidak keberatan dengan regex, Anda juga dapat melakukannyareturn result.map(file => file.replace(/\\/g, '/'));
SwiftNinjaPro
106

Kalau-kalau ada yang merasa berguna, saya juga membuat versi sinkron .

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

Kiat: Untuk menggunakan lebih sedikit sumber daya saat memfilter. Saring di dalam fungsi ini sendiri. Misalnya Ganti results.push(file);dengan kode di bawah ini. Sesuaikan sesuai kebutuhan:

    file_type = file.split(".").pop();
    file_name = file.split(/(\\|\/)/g).pop();
    if (file_type == "json") results.push(file);
Victor Powell
sumber
60
Saya suka solusi ini, kecuali Anda kekurangan semi-titik dua!
mpen
Ini sederhana. Tetapi juga agak naif. Mungkin menyebabkan stackoverflow jika direktori berisi tautan ke direktori induk. Mungkin menggunakan lstatsebagai gantinya? Atau tambahkan cek rekursif untuk membatasi tingkat rekursif.
conradkleinespel
14
Pertimbangkan untuk menggunakan file = require ("path"). Join (dir, file)
mkamioner
16
@mpen Semi-titik dua berlebihan
Ally
Ini juga bekerja paling baik untuk saya. Padahal saya juga menambahkan filter untuk memfilter untuk ekstensi file tertentu.
Brian
87

A. Lihat modul file . Ini memiliki fungsi yang disebut berjalan:

file.walk (mulai, panggil balik)

Menavigasi pohon file, memanggil panggilan balik untuk setiap direktori, lewat (null, dirPath, dirs, file).

Ini mungkin untukmu! Dan ya, itu async. Namun, saya pikir Anda harus mengumpulkan sendiri jalur lengkapnya, jika Anda membutuhkannya.

B. Alternatif, dan bahkan salah satu favorit saya: gunakan unix finduntuk itu. Mengapa melakukan sesuatu lagi, yang sudah diprogram? Mungkin tidak persis apa yang Anda butuhkan, tetapi masih layak untuk dicoba:

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 memiliki mekanisme caching bawaan yang bagus yang membuat pencarian berikutnya sangat cepat, selama hanya beberapa folder yang berubah.

Johann Philipp Strathausen
sumber
9
Apakah ini UNIX saja?
Mohsen
Punya pertanyaan tentang contoh B: Untuk execFile () (dan exec ()) stderr dan stdout adalah Buffer .. jadi Anda tidak perlu melakukan stdout.toString.split ("\ n") karena Buffer bukan Strings?
Cheruvim
8
bagus, tetapi tidak lintas platform.
f0ster
Ngomong-ngomong: Tidak, A bukan hanya Unix! Hanya B yang merupakan Unix saja. Namun, Windows 10 sekarang hadir dengan subsistem Linux. Jadi, bahkan B hanya akan berfungsi pada Windows saat ini.
Johann Philipp Strathausen
bukankah WSL harus diaktifkan pada PC pengguna akhir agar bisa bekerja di Windows ??
oldboy
38

Paket npm yang bagus adalah glob .

npm install glob

Ini sangat kuat dan harus mencakup semua kebutuhan berulang Anda.

Edit:

Saya sebenarnya tidak sepenuhnya senang dengan glob, jadi saya membuat readdirp .

Saya sangat yakin bahwa API-nya membuat pencarian file dan direktori secara rekursif dan menerapkan filter spesifik menjadi sangat mudah.

Bacalah dokumentasinya untuk mendapatkan ide yang lebih baik tentang apa yang dilakukannya dan instal melalui:

npm install readdirp

Thorsten Lorenz
sumber
Modul terbaik menurut saya. Dan sama banyak proyek lainnya, seperti Grunt, Mocha, dll. Dan 80'000 + proyek lainnya. Hanya mengatakan.
Yanick Rochon
29

Saya sarankan menggunakan node-glob untuk menyelesaikan tugas itu.

var glob = require( 'glob' );  

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

Jika Anda ingin menggunakan paket npm, kunci pas cukup bagus.

var wrench = require("wrench");

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

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

EDIT (2018):
Siapa pun yang membaca dalam waktu terakhir: Penulis mencabut paket ini pada tahun 2015:

wrench.js sudah usang, dan belum diperbarui dalam beberapa waktu. Saya sangat merekomendasikan menggunakan fs-extra untuk melakukan operasi sistem file tambahan.

Domenik
sumber
@Domenic, bagaimana kabarmu denodifyini? Callback dipecat beberapa kali (secara rekursif). Jadi menggunakan Q.denodify(wrench.readdirRecursive)pengembalian hanya hasil pertama.
Onur Yıldırım
1
@ OnurYıldırım ya, ini tidak cocok untuk janji apa adanya. Anda perlu menulis sesuatu yang mengembalikan banyak janji, atau sesuatu yang menunggu sampai semua subdir disebutkan sebelum mengembalikan janji. Untuk yang terakhir, lihat github.com/kriskowal/q-io#listdirectorytreepath
Domenic
9

Saya menyukai jawaban dari chjj di atas dan tidak akan dapat membuat versi saya dari loop paralel tanpa permulaan itu.

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;

Saya membuat intisari juga. Komentar diterima. Saya masih memulai di ranah NodeJS sehingga itu adalah salah satu cara saya berharap untuk belajar lebih banyak.

kalisjoshua
sumber
9

Dengan Rekursi

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

Panggilan

getFiles(path, files)
console.log(files) // will log all files in directory
Loourr
sumber
3
Saya sarankan tidak bergabung dengan string path dengan /tetapi menggunakan pathmodul: path.join(searchPath, file). Dengan begitu, Anda akan mendapatkan jalur yang benar terlepas dari OS.
Moritz Friedrich
8

Gunakan node-dir untuk menghasilkan output yang Anda suka

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);
  })
});
Christiaan Westerbeek
sumber
node-dir bekerja dengan baik, tetapi ketika saya menggunakannya dengan webpack saya memiliki beberapa masalah aneh. Â dimasukkan dalam fungsi readFiles seperti pada "if (err) Â {" menyebabkan "SyntaxError yang tidak tertangkap: Kesalahan token {" yang tidak terduga. Saya bingung dengan masalah ini dan reaksi langsung saya adalah mengganti node-dir dengan sesuatu yang serupa
Parth
1
@Bagikan komentar ini tidak akan memberi Anda jawaban. Tulis pertanyaan lengkap baru pada SO atau buat masalah di repositori GitHub. Ketika Anda menguraikan pertanyaan Anda dengan baik, Anda bahkan mungkin dapat menyelesaikan masalah Anda tanpa harus mempostingnya
Christiaan Westerbeek
1
Komentar @ Parth mungkin masih menjadi peringatan yang berguna bagi orang lain yang mempertimbangkan saran Anda sebagai solusi untuk masalah mereka. Mereka mungkin tidak mencari jawaban di bagian komentar ini :)
4

Saya telah mengkodekan ini baru-baru ini, dan berpikir akan masuk akal untuk membagikan ini di sini. Kode memanfaatkan perpustakaan async .

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

Anda bisa menggunakannya seperti ini:

scan('/some/dir', '.ext', function(err, files) {
  // Do something with files that ends in '.ext'.
  console.log(files);
});
residif
sumber
2
Ini. Ini sangat rapi dan mudah digunakan. Saya memompakannya ke dalam modul, membutuhkannya dan berfungsi seperti sandwich mcdream.
Jay
4

Perpustakaan yang disebut Filehound adalah pilihan lain. Ini akan secara rekursif mencari direktori yang diberikan (direktori kerja secara default). Ini mendukung berbagai filter, panggilan balik, janji dan sinkronisasi pencarian.

Misalnya, cari direktori kerja saat ini untuk semua file (menggunakan panggilan balik):

const Filehound = require('filehound');

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

Atau menjanjikan dan menentukan direktori tertentu:

const Filehound = require('filehound');

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

Konsultasikan dengan dokumen untuk kasus penggunaan lebih lanjut dan contoh penggunaan: https://github.com/nspragg/filehound

Penafian: Saya penulis.

nickool
sumber
4

Menggunakan async / menunggu, ini seharusnya bekerja:

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

Anda dapat menggunakan bluebird. Promisikan atau ini:

/**
 * 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+ memiliki Promisify built-in

Lihat jawaban saya yang lain untuk pendekatan generator yang dapat memberikan hasil lebih cepat.

Mpen
sumber
4

Async

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)

Sinkronkan

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

Dapat dibaca Async

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)

Catatan: kedua versi akan mengikuti symlink (sama seperti aslinya fs.readdir)

Afanasii Kurakin
sumber
3

Lihatlah perpustakaan final-fs . Ini menyediakan readdirRecursivefungsi:

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
    });
Szymon Wygnański
sumber
2

Implementasi janji mandiri

Saya menggunakan perpustakaan when.js berjanji dalam contoh ini.

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

Saya telah memasukkan parameter opsional includeDiryang akan menyertakan direktori dalam daftar file jika diatur ke true.

JayQuerie.com
sumber
1

Inilah implementasi lain . Tak satu pun dari solusi di atas memiliki pembatas, dan jadi jika struktur direktori Anda besar, mereka semua akan meronta-ronta dan akhirnya kehabisan sumber daya.

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

Menggunakan konkurensi 50 berfungsi dengan baik, dan hampir secepat implementasi sederhana untuk struktur direktori kecil.

Boson monyet
sumber
1

Saya memodifikasi jawaban berdasarkan Trevor Senior's Promise untuk bekerja dengan Bluebird

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

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

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

Untuk bersenang-senang, berikut adalah versi berbasis aliran yang bekerja dengan perpustakaan aliran highland.js. Itu ditulis bersama oleh 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))
Michael Connor
sumber
1

Menggunakan Janji ( Q ) untuk menyelesaikan ini dalam gaya Fungsional:

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

Ini mengembalikan janji array, sehingga Anda dapat menggunakannya sebagai:

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

Saya harus menambahkan perpustakaan sander berbasis Janji ke daftar.

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

Menggunakan bluebird janji.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));
alexcres
sumber
0

Karena setiap orang harus menulis sendiri, saya membuatnya.

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

KOTOR

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

  });
}
vvo
sumber
0

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

Anda dapat menggunakan fileNamealih-alih baseNamejika Anda membutuhkan ekstensi juga.

Bonus tambahan adalah bahwa ia akan menonton file juga dan memanggil panggilan balik lagi. Ada banyak opsi konfigurasi untuk membuatnya sangat fleksibel.

Saya baru saja membuat ulang guardpermata dari ruby ​​menggunakan loaddir dalam waktu singkat

Funkodebat
sumber
0

Ini jawaban saya. Semoga bisa membantu seseorang.

Fokus saya adalah membuat pencarian rutin dapat berhenti di mana saja, dan untuk file yang ditemukan, memberitahu kedalaman relatif ke jalur asli.

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);
manbaum
sumber
0

Berikut adalah metode rekursif untuk mendapatkan semua file termasuk subdirektori.

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;
}
Daniel
sumber
0

Satu lagi yang sederhana dan bermanfaat

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;
    }
}
Clinyong
sumber
Anda mengasumsikan bahwa setiap file di direktori root adalah folder di sini.
xechelonx
0

Ini adalah bagaimana saya menggunakan fungsi nodejs fs.readdir untuk mencari direktori secara rekursif.

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

Katakanlah Anda memiliki jalur yang disebut '/ database' di root proyek simpul Anda. Setelah janji ini terselesaikan, harus memunculkan array dari setiap file di bawah '/ database'.

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