ricerca directory ricorsiva node.js fs.readdir


268

Qualche idea su una ricerca asincrona nella directory usando fs.readdir? Mi rendo conto che potremmo introdurre la ricorsione e chiamare la funzione read directory con la directory successiva da leggere, ma sono un po 'preoccupato che non sia asincrono ...

Qualche idea? Ho osservato node-walk che è fantastico, ma non mi dà solo i file in un array, come fa readdir. Sebbene

Alla ricerca di output come ...

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

Risposte:


379

Esistono sostanzialmente due modi per ottenere questo risultato. In un ambiente asincrono noterai che ci sono due tipi di loop: seriale e parallelo. Un loop seriale attende il completamento di un'iterazione prima di passare all'iterazione successiva, ciò garantisce che ogni iterazione del loop venga completata in ordine. In un ciclo parallelo, tutte le iterazioni vengono avviate contemporaneamente e una può essere completata prima di un'altra, tuttavia è molto più veloce di un ciclo seriale. Quindi, in questo caso, è probabilmente meglio usare un ciclo parallelo perché non importa in quale ordine completa la camminata, purché completi e restituisca i risultati (a meno che non li desideri in ordine).

Un ciclo parallelo sarebbe simile al seguente:

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

Un ciclo seriale sarebbe simile al seguente:

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

E per testarlo sulla tua home directory (ATTENZIONE: l'elenco dei risultati sarà enorme se hai un sacco di cose nella tua home directory):

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

EDIT: esempi migliorati.


10
Attenzione, la risposta "loop parallelo" di chjj sopra ha un bug nei casi in cui si cammina una cartella vuota. La correzione è: var pending = list.length; if (! pending) done (null, risultati); // aggiungi questa linea! list.forEach (funzione (file) {...
Vasil Daskalopoulos

27
file = dir + '/' + file;Questo non è raccomandato Dovresti usare: var path = require('path'); file = path.resolve(dir, file);
Leiko il

7
@onetrickpony perché se lo usi path.resolve(...)otterrai un percorso corretto su Windows o Unix :) Ciò significa che otterrai qualcosa di simile C:\\some\\foo\\pathsu Windows e /some/foo/pathsui sistemi Unix
Leiko

19
Ho annullato il voto perché la tua risposta è stata fantastica quando l'hai scritta per la prima volta nel 2011, ma nel 2014 le persone usano moduli open source e scrivono meno codice e contribuiscono ai moduli da cui dipendono da loro e da tante altre persone. Ad esempio, prova node-dir per ottenere esattamente l'output richiesto da @crawf usando questa riga di codice:require('node-dir').files(__dirname, function(err, files) { console.log(files); });
Christiaan Westerbeek,

5
Per chiunque sia confuso sulla !--sintassi, è stata posta una domanda al riguardo
Tas

147

Questo utilizza la massima quantità di nuove funzionalità disponibili nel nodo 8, tra cui Promesse, util / promisify, destructuring, async-waitit, map + ridurre e altro ancora, facendo in modo che i tuoi colleghi si grattino la testa mentre cercano di capire cosa sta succedendo.

Nodo 8+

Nessuna dipendenza esterna.

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

uso

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

Nodo 10.10+

Aggiornato per il nodo 10+ con ancora più whizbang:

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

async function getFiles(dir) {
  const dirents = await readdir(dir, { withFileTypes: true });
  const files = await Promise.all(dirents.map((dirent) => {
    const res = resolve(dir, dirent.name);
    return dirent.isDirectory() ? getFiles(res) : res;
  }));
  return Array.prototype.concat(...files);
}

Si noti che a partire dal nodo 11.15.0 è possibile utilizzare files.flat()anziché Array.prototype.concat(...files)appiattire l'array di file.

Nodo 11+

Se vuoi far esplodere completamente la testa di tutti, puoi usare la seguente versione usando iteratori asincroni . Oltre ad essere davvero interessante, consente anche ai consumatori di ottenere risultati una volta alla volta, rendendolo più adatto per directory molto grandi.

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

L'utilizzo è cambiato perché il tipo restituito è ora un iteratore asincrono anziché una promessa

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

Nel caso qualcuno fosse interessato, ho scritto di più sugli iteratori asincroni qui: https://qwtel.com/posts/software/async-generators-in-the-wild/


5
La denominazione di subdire subdirsè fuorviante, in quanto questi possono essere in realtà dei file (vi consiglio qualcosa di simile itemInDiro item_in_diro anche semplicemente item, invece.), Ma questa soluzione si sente più pulito di quello accettato ed è molto codice meno. Inoltre non lo trovo molto più complicato del codice nella risposta accettata. +1
Zelphir Kaltstahl,

1
Puoi renderlo ancora più sfrenato usando require(fs).promisese semplicemente abbandonando util.promisifycompletamente. Personalmente, alias fs a promesse.
MushinNoShin,

2
Possiamo farlo più velocemente con una piccola modifica: passando il secondo argomento ad readdirAKA l'oggetto opzioni in modo readdir(dir, {withFileTypes: true})tale da restituire tutti gli elementi con le loro informazioni sul tipo, quindi non avremo bisogno di chiamare statper ottenere le informazioni che readdirora ci danno indietro. Questo ci evita di dover effettuare chiamate di sistema aggiuntive. Dettagli qui
cacoder

1
@cacoder Aggiornato per includere withFileTypes. Grazie per il consiglio.
qwtel,

nel nodo 10.10+, se si sostituisce return Array.prototype.concat(...files);con let result = Array.prototype.concat(...files); return result.map(file => file.split('\\').join('/'));è possibile assicurarsi che le directory restituiscano un "/" e non un "\". Se non ti dispiace regex, puoi anche farloreturn result.map(file => file.replace(/\\/g, '/'));
SwiftNinjaPro

106

Nel caso in cui qualcuno lo trovi utile, ho anche messo insieme una versione sincrona .

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

Suggerimento: utilizzare meno risorse durante il filtraggio. Filtro all'interno di questa stessa funzione. Ad esempio, sostituire results.push(file);con il codice seguente. Regola come richiesto:

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

60
Mi piace questa soluzione, fatta eccezione per la tua mancanza di punti e virgola!
Aprire il

Questo è semplice Ma anche un po 'ingenuo. Potrebbe causare uno stackoverflow se una directory contiene un collegamento a una directory padre. Forse usare lstatinvece? Oppure aggiungi un controllo di ricorsività per limitare il livello di ricorsività.
conradkleinespel,

14
Prendi in considerazione l'utilizzo di file =
request

16
@mpen I punti e virgola sono ridondanti
Alleato il

Questo funziona anche meglio per me. Anche se ho anche aggiunto un filtro per filtrare un'estensione di file specifica.
Brian

87

A. Dai un'occhiata al modulo file . Ha una funzione chiamata walk:

file.walk (start, callback)

Naviga in un albero di file, chiamando il callback per ogni directory, passando (null, dirPath, dirs, file).

Questo potrebbe essere per te! E sì, è asincrono. Tuttavia, penso che dovresti aggregare tu stesso l'intero percorso, se ne avessi bisogno.

B. Un'alternativa, e anche una delle mie preferite: usa l'unix findper quello. Perché fare di nuovo qualcosa, che è già stato programmato? Forse non è esattamente quello che ti serve, ma vale comunque la pena dare un'occhiata:

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 ha un bel meccanismo di memorizzazione nella cache che rende le ricerche successive molto veloci, purché siano cambiate solo poche cartelle.


9
Questo è solo UNIX?
Mohsen,

Hai avuto una domanda sull'esempio B: Per execFile () (ed exec ()) stderr e stdout sono buffer .. quindi non avresti bisogno di fare stdout.toString.split ("\ n") poiché i buffer non sono stringhe?
Cheruvim,

8
bello, ma non multipiattaforma.
dal

A proposito: No, A non è solo Unix! Solo B è solo Unix. Tuttavia, Windows 10 ora viene fornito con un sottosistema Linux. Quindi anche B funzionerebbe su Windows al giorno d'oggi.
Johann Philipp Strathausen,

WSL non dovrebbe essere abilitato sul PC degli utenti finali perché funzioni su Windows ??
oldboy

38

Un altro bel pacchetto npm è glob .

npm install glob

È molto potente e dovrebbe coprire tutte le tue esigenze ricorrenti.

Modificare:

In realtà non ero perfettamente soddisfatto di glob, quindi ho creato readdirp .

Sono molto fiducioso che la sua API rende molto semplice la ricerca ricorsiva di file e directory e l'applicazione di filtri specifici.

Leggi la sua documentazione per avere un'idea più chiara di ciò che fa e installa tramite:

npm install readdirp


Il miglior modulo secondo me. Ed è simile a molti altri progetti, come Grunt, Mocha, ecc. E altri 80.000 + altri progetti. Sto solo dicendo.
Yanick Rochon,

29

Raccomando di usare node-glob per svolgere questo compito.

var glob = require( 'glob' );  

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

14

Se vuoi usare un pacchetto npm, wrench è abbastanza buono.

var wrench = require("wrench");

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

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

EDIT (2018):
chiunque abbia letto negli ultimi tempi: l'autore ha deprecato questo pacchetto nel 2015:

wrench.js è obsoleto e non è stato aggiornato da un po 'di tempo. Consiglio vivamente di usare fs-extra per fare qualsiasi operazione extra sul filesystem.


@Domenic, come fai denodify? Il callback viene attivato più volte (in modo ricorsivo). Quindi l'utilizzo Q.denodify(wrench.readdirRecursive)restituisce solo il primo risultato.
Onur Yıldırım,

1
@ OnurYıldırım sì, questa non è una buona scelta per le promesse così come sono. Dovresti scrivere qualcosa che restituisca più promesse o qualcosa che attende fino a quando tutti i sottodirectory sono elencati prima di restituire una promessa. Per quest'ultimo, vedi github.com/kriskowal/q-io#listdirectorytreepath
Domenic

9

Ho adorato la risposta da Chjj sopra e non sarei stato in grado di creare la mia versione del ciclo parallelo senza quell'avvio .

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;

Ho anche creato un Gist . Commenti benvenuti. Sto ancora iniziando nel regno NodeJS, quindi è un modo in cui spero di saperne di più.


9

Con Ricorsione

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

chiamata

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

3
Io suggerirei che non si aggiungono le stringhe di percorso con /ma usando il pathmodulo: path.join(searchPath, file). In questo modo, otterrai percorsi corretti indipendentemente dal sistema operativo.
Moritz Friedrich,

8

Usa node-dir per produrre esattamente l'output che ti piace

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 funzionava bene, ma quando l'ho usato con il webpack ho alcuni strani problemi. Un  viene inserito nella funzione readFiles come in "if (err)  {" causando un errore "SyntaxError non rilevato: token {" imprevisto. Sono sconcertato da questo problema e la mia reazione immediata è quella di sostituire node-dir con qualcosa di simile
Parth,

1
@Parth questo commento non ti darà risposte. Scrivi una nuova domanda completa su SO o crea un problema nel repository GitHub. Quando elaborerai bene la tua domanda, potresti persino essere in grado di risolvere il tuo problema senza nemmeno doverlo pubblicare
Christiaan Westerbeek,

1
Il commento di @ Parth può ancora essere un avvertimento utile per gli altri che stanno considerando il tuo suggerimento come la soluzione al loro problema. Potrebbero non aver cercato una risposta in questa sezione dei commenti :)

4

L'ho codificato di recente e ho pensato che avrebbe senso condividerlo qui. Il codice utilizza la libreria asincrona .

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

Puoi usarlo in questo modo:

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

2
Questo. Questo è così ordinato e semplice da usare. L'ho pompato in un modulo, l'ho richiesto e funziona come un sandwich mcdream.
Jay,

4

Una libreria chiamata Filehound è un'altra opzione. Cercherà ricorsivamente una data directory (directory di lavoro per impostazione predefinita). Supporta vari filtri, callback, promesse e ricerche di sincronizzazione.

Ad esempio, cerca nella directory di lavoro corrente tutti i file (usando i callback):

const Filehound = require('filehound');

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

O promesse e specificando una directory specifica:

const Filehound = require('filehound');

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

Consultare i documenti per ulteriori casi d'uso ed esempi di utilizzo: https://github.com/nspragg/filehound

Disclaimer: sono l'autore.


4

Usando async / await, questo dovrebbe funzionare:

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

Puoi usare bluebird.Promisify o questo:

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

Il nodo 8+ ha Promisify integrato

Vedi la mia altra risposta per un approccio generatore che può dare risultati ancora più velocemente.


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)

Sync

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

Leggibile asincrono

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)

Nota: entrambe le versioni seguiranno i collegamenti simbolici (uguale all'originale fs.readdir)


3

Dai un'occhiata alla libreria di final-fs . Fornisce una readdirRecursivefunzione:

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

Implementazione autonoma delle promesse

In questo esempio sto usando la libreria di promesse when.js.

var fs = require('fs')
, path = require('path')
, when = require('when')
, nodefn = require('when/node/function');

function walk (directory, includeDir) {
    var results = [];
    return when.map(nodefn.call(fs.readdir, directory), function(file) {
        file = path.join(directory, file);
        return nodefn.call(fs.stat, file).then(function(stat) {
            if (stat.isFile()) { return results.push(file); }
            if (includeDir) { results.push(file + path.sep); }
            return walk(file, includeDir).then(function(filesInDir) {
                results = results.concat(filesInDir);
            });
        });
    }).then(function() {
        return results;
    });
};

walk(__dirname).then(function(files) {
    console.log(files);
}).otherwise(function(error) {
    console.error(error.stack || error);
});

Ho incluso un parametro opzionale includeDirche includerà le directory nell'elenco dei file se impostato su true.



1

Ecco un'altra implementazione. Nessuna delle soluzioni di cui sopra ha limiti, quindi se la struttura della directory è grande, si romperanno e alla fine esauriranno le risorse.

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

L'uso di una concorrenza di 50 funziona abbastanza bene ed è quasi altrettanto veloce delle implementazioni più semplici per strutture di directory di piccole dimensioni.



1

Ho modificato la risposta basata su Promessa di Trevor Senior per lavorare con 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); {
});

1

Per divertimento, ecco una versione basata sul flusso che funziona con la libreria di flussi highland.js. È stato scritto da 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

Utilizzo di Promises ( Q ) per risolverlo in uno stile funzionale:

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

Restituisce una promessa di un array, quindi puoi usarlo come:

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

1

Devo aggiungere la libreria di levigatrici basata su Promise all'elenco.

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

1

Utilizzando 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

Perché tutti dovrebbero scrivere il proprio, ne ho creato uno.

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

SPORCO

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

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

È possibile utilizzare fileNameinvece baseNamese è necessaria anche l'estensione.

Un ulteriore vantaggio è che controllerà anche i file e richiamerà nuovamente la richiamata. Ci sono tonnellate di opzioni di configurazione per renderlo estremamente flessibile.

Ho appena rifatto la guardgemma dal rubino usando loaddir in breve tempo


0

Questa è la mia risposta Spero che possa aiutare qualcuno.

Il mio obiettivo è quello di far fermare la routine di ricerca in qualsiasi luogo e, per un file trovato, indica la profondità relativa al percorso originale.

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

Ecco un metodo ricorsivo per ottenere tutti i file comprese le sottodirectory.

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

Un altro semplice e utile

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

Stai assumendo che ogni file nella directory principale sia una cartella qui.
xechelonx,

0

Questo è il modo in cui uso la funzione nodejs fs.readdir per cercare ricorsivamente una directory.

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

Supponiamo che tu abbia un percorso chiamato '/ database' nella radice dei tuoi progetti nodo. Una volta risolta questa promessa, dovrebbe sputare una matrice di ogni file in "/ database".

readdirRecursivePromise('database').then(out => {
    console.log(flatten(out));
}).catch(err => {
    console.log(err);
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.