Istnieją w zasadzie dwa sposoby osiągnięcia tego. W środowisku asynchronicznym zauważysz, że istnieją dwa rodzaje pętli: szeregowa i równoległa. Pętla szeregowa czeka na zakończenie jednej iteracji, zanim przejdzie do następnej iteracji - gwarantuje to, że każda iteracja pętli zakończy się w kolejności. W pętli równoległej wszystkie iteracje rozpoczynają się w tym samym czasie i można je wykonać przed drugą, jednak jest to znacznie szybsze niż pętla szeregowa. Tak więc w tym przypadku prawdopodobnie lepiej jest użyć pętli równoległej, ponieważ nie ma znaczenia, w jakiej kolejności kończy się spacer, pod warunkiem, że zakończy się i zwróci wyniki (chyba że chcesz je w kolejności).
Pętla równoległa wyglądałaby tak:
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);
}
});
});
});
};
Pętla szeregowa wyglądałaby tak:
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();
}
});
})();
});
};
I aby przetestować go w swoim katalogu domowym (UWAGA: lista wyników będzie ogromna, jeśli masz wiele rzeczy w katalogu domowym):
walk(process.env.HOME, function(err, results) {
if (err) throw err;
console.log(results);
});
EDYCJA: Ulepszone przykłady.
file = dir + '/' + file;
Nie jest to zalecane. Powinieneś użyć:var path = require('path'); file = path.resolve(dir, file);
path.resolve(...)
, uzyskasz właściwą ścieżkę, niezależnie od tego, czy korzystasz z systemu Windows, czy Unix :) Oznacza to, że dostaniesz coś takiego jakC:\\some\\foo\\path
w systemie Windows i/some/foo/path
na systemach Unixrequire('node-dir').files(__dirname, function(err, files) { console.log(files); });
!--
składni, zadano pytanieTa wykorzystuje maksymalną liczbę nowych, modnych funkcji dostępnych w węźle 8, w tym Obietnice, wykorzystanie / obiecanie, destrukcję, asynchroniczne oczekiwanie, mapę + redukcję i inne, co powoduje, że współpracownicy drapią się po głowie, gdy próbują dowiedzieć się, co trwa.
Węzeł 8+
Brak zależności zewnętrznych.
Stosowanie
Węzeł 10.10+
Zaktualizowany dla węzła 10+ z jeszcze większym whizbangiem:
Zauważ, że zaczynając od węzła 11.15.0, możesz użyć
files.flat()
zamiastArray.prototype.concat(...files)
spłaszczenia tablicy plików.Węzeł 11+
Jeśli chcesz całkowicie wysadzić wszystkich w powietrze, możesz użyć następującej wersji przy użyciu iteratorów asynchronicznych . Oprócz tego, że jest naprawdę fajny, pozwala również konsumentom wyciągać wyniki pojedynczo, dzięki czemu lepiej nadaje się do naprawdę dużych katalogów.
Użycie uległo zmianie, ponieważ typ zwracany jest teraz iteratorem asynchronicznym zamiast obietnicy
Jeśli ktoś jest zainteresowany, pisałem więcej o iteratorach asynchronicznych tutaj: https://qwtel.com/posts/software/async-generators-in-the-wild/
źródło
subdir
isubdirs
wprowadza w błąd, gdyż może to być rzeczywiście plików (proponuję coś podobnegoitemInDir
lubitem_in_dir
nawet po prostuitem
zamiast.), Ale rozwiązanie to czuje się czystsze niż akceptowanego jeden i jest znacznie mniej kodu. Nie uważam też, że jest to o wiele bardziej skomplikowane niż kod w zaakceptowanej odpowiedzi. +1require(fs).promises
i po prostuutil.promisify
całkowicie upuszczając . Osobiście pseudonim od fs do fs.promises.readdir
obiektu opcji AKA tak, abyreaddir(dir, {withFileTypes: true})
zwrócił wszystkie elementy z informacjami o ich typie, więc nie będziemy musielistat
w ogóle dzwonić, aby uzyskać informacje, którereaddir
teraz dają nam plecy. To oszczędza nam konieczności wykonywania dodatkowych połączeń sys. Szczegóły tutajwithFileTypes
. Dzięki za wskazówkę.return Array.prototype.concat(...files);
zlet result = Array.prototype.concat(...files); return result.map(file => file.split('\\').join('/'));
was może mieć pewność, że katalogi zwróci „/”, a nie „\”. Jeśli nie masz nic przeciwko wyrażeniom regularnym, możesz to zrobićreturn result.map(file => file.replace(/\\/g, '/'));
Na wypadek, gdyby ktoś uznał to za przydatne, stworzyłem również wersję synchroniczną .
Wskazówka: Aby zużywać mniej zasobów podczas filtrowania. Filtruj w obrębie tej funkcji. Np. Zamień na
results.push(file);
poniższy kod. Dostosuj zgodnie z wymaganiami:źródło
lstat
zamiast tego użyć ? Albo dodaj kontrolę rekurencyjności, aby ograniczyć poziom rekurencyjności.A. Spójrz na moduł plików . Ma funkcję o nazwie spacer:
To może być dla ciebie! I tak, to asynchronizacja. Myślę jednak, że gdybyś potrzebował, musiałbyś sam zagregować całą ścieżkę.
B. Alternatywa, a nawet jedna z moich ulubionych: użyj do tego unixa
find
. Po co robić coś, co zostało już zaprogramowane? Może nie dokładnie to, czego potrzebujesz, ale nadal warto sprawdzić:Find ma ładny wbudowany mechanizm buforowania, który sprawia, że kolejne wyszukiwania są bardzo szybkie, o ile zmieniło się tylko kilka folderów.
źródło
Kolejnym ładnym pakietem npm jest glob .
npm install glob
Jest bardzo potężny i powinien pokryć wszystkie Twoje rekurencyjne potrzeby.
Edytować:
Tak naprawdę nie byłem całkowicie zadowolony z glob, więc stworzyłem readdirp .
Jestem przekonany, że jego interfejs API ułatwia rekurencyjne wyszukiwanie plików i katalogów oraz stosowanie określonych filtrów.
Przeczytaj dokumentację, aby uzyskać lepszy obraz tego, co robi, i zainstaluj:
npm install readdirp
źródło
Polecam użycie node-glob do wykonania tego zadania.
źródło
Jeśli chcesz użyć pakietu npm, klucz jest całkiem niezły.
EDYCJA (2018):
Każdy, kto czytał w ostatnim czasie: Autor wycofał ten pakiet w 2015 roku:
źródło
denodify
? Oddzwanianie jest uruchamiane wiele razy (rekurencyjnie). Zatem użycieQ.denodify(wrench.readdirRecursive)
zwraca tylko pierwszy wynik.Podobało mi się powyższe pytanie od chjj i bez tego początku nie byłbym w stanie stworzyć mojej wersji równoległej pętli.
Stworzyłem również Gist . Komentarze mile widziane. Wciąż zaczynam w dziedzinie NodeJS, więc mam nadzieję, że dowiem się więcej.
źródło
Z rekurencją
Powołanie
źródło
/
ale przy użyciupath
modułu:path.join(searchPath, file)
. W ten sposób uzyskasz prawidłowe ścieżki niezależnie od systemu operacyjnego.Użyj node-dir, aby uzyskać dokładnie taki wynik, jaki ci się podoba
źródło
Napisałem to niedawno i pomyślałem, że warto udostępnić to tutaj. Kod korzysta z biblioteki asynchronicznej .
Możesz użyć tego w następujący sposób:
źródło
Inną opcją jest biblioteka o nazwie Filehound . Rekurencyjnie przeszuka dany katalog (domyślnie katalog roboczy). Obsługuje różne filtry, połączenia zwrotne, obietnice i wyszukiwania synchronizacji.
Na przykład wyszukaj w bieżącym katalogu roboczym wszystkie pliki (używając wywołań zwrotnych):
Lub obietnice i określenie konkretnego katalogu:
Zapoznaj się z dokumentacją w celu uzyskania dalszych przykładów użycia i przykładów użycia: https://github.com/nspragg/filehound
Oświadczenie: Jestem autorem.
źródło
Przy użyciu async / await powinno to działać:
Możesz użyć bluebird.Promisify lub tego:
Zobacz moją drugą odpowiedź na podejście generatora, które może dać wyniki jeszcze szybciej.
źródło
Asynchronizacja
Synchronizacja
Czytelne asynchronicznie
Uwaga: obie wersje będą podążać za dowiązaniami symbolicznymi (tak samo jak oryginał
fs.readdir
)źródło
Sprawdź bibliotekę final-fs . Zapewnia
readdirRecursive
funkcję:źródło
Samodzielna realizacja obietnicy
Korzystam z biblioteki obietnicy when.js w tym przykładzie.
Podałem opcjonalny parametr,
includeDir
który będzie zawierał katalogi na liście plików, jeśli jest ustawiony natrue
.źródło
Klaw i klaw-sync są warte rozważenia dla tego rodzaju rzeczy. Były one częścią węzła-fs-extra .
źródło
Oto kolejna implementacja. Żadne z powyższych rozwiązań nie ma żadnych ograniczeń, więc jeśli twoja struktura katalogów jest duża, wszystkie będą druzgocić i ostatecznie zabraknie zasobów.
Korzystanie z współbieżności 50 działa całkiem dobrze i jest prawie tak szybkie, jak prostsze implementacje dla małych struktur katalogów.
źródło
Moduł recursive-readdir ma tę funkcjonalność.
źródło
Zmodyfikowałem odpowiedź Trevor Senior Promise na współpracę z Bluebird
źródło
Dla zabawy, oto wersja oparta na przepływach, która współpracuje z biblioteką strumieni highland.js. Współautorem jest Victor Vu.
źródło
Używanie obietnic ( Q ) do rozwiązania tego w funkcjonalnym stylu:
Zwraca obietnicę tablicy, dzięki czemu można jej użyć jako:
źródło
Muszę dodać bibliotekę szlifierki opartą na obietnicy do listy.
źródło
Za pomocą bluebird promise.coroutine:
źródło
Ponieważ każdy powinien pisać własne, stworzyłem jeden.
walk (dir, cb, endCb) cb (plik) endCb (err | null)
BRUDNY
źródło
sprawdź loaddir https://npmjs.org/package/loaddir
npm install loaddir
Możesz użyć
fileName
zamiast,baseName
jeśli potrzebujesz również rozszerzenia.Dodatkową zaletą jest to, że będzie także oglądać pliki i ponownie wywoływać oddzwonienie. Istnieje mnóstwo opcji konfiguracji, dzięki którym jest niezwykle elastyczny.
Właśnie przerobiłem
guard
klejnot z rubinu za pomocą loaddir w krótkim czasieźródło
To jest moja odpowiedź. Mam nadzieję, że to może komuś pomóc.
Skupiam się na tym, aby procedura wyszukiwania mogła zatrzymać się w dowolnym miejscu, a dla znalezionego pliku określa względną głębokość oryginalnej ścieżki.
źródło
Oto rekurencyjna metoda uzyskiwania wszystkich plików, w tym podkatalogów.
źródło
Kolejny prosty i pomocny
źródło
W ten sposób używam funkcji fs.readdir nodejs do rekurencyjnego przeszukiwania katalogu.
Załóżmy, że masz ścieżkę o nazwie „/ database” w katalogu głównym projektów węzłów. Po rozwiązaniu tej obietnicy powinien wypluć tablicę każdego pliku w „/ database”.
źródło