簡體   English   中英

Node.js 遞歸列出文件的完整路徑

[英]Node.js recursively list full path of files

各位晚安。 我可能遇到了一些簡單的遞歸函數的問題。 問題是遞歸列出給定文件夾及其子文件夾中的所有文件。

目前,我已經設法使用一個簡單的函數列出目錄中的文件:

fs.readdirSync(copyFrom).forEach((file) => {
  let fullPath = path.join(copyFrom, file);

  if (fs.lstatSync(fullPath).isDirectory()) {
    console.log(fullPath);
  } else {
    console.log(fullPath);
  }
});

我已經嘗試過各種方法,例如do{} ... while()但我做錯了。 由於我是javascript的初學者,我終於決定向你們尋求幫助。

只需添加一個遞歸調用,您就完成了:

 function traverseDir(dir) {
   fs.readdirSync(dir).forEach(file => {
     let fullPath = path.join(dir, file);
     if (fs.lstatSync(fullPath).isDirectory()) {
        console.log(fullPath);
        traverseDir(fullPath);
      } else {
        console.log(fullPath);
      }  
   });
 }

以這種方式使用console.log會顯示路徑,這很好,但是如果您想對路徑做一些更有意義的事情怎么辦? 例如,也許您想將它們全部收集到一個數組中並將它們傳遞給其他地方處理......

這個從種子狀態開始並在狀態變化時擴展一系列值的過程稱為unfold

const { join } =
  require ('path')

const { readdirSync, statSync } =
  require ('fs')

const unfold = (f, initState) =>
  f ( (value, nextState) => [ value, ...unfold (f, nextState) ]
    , () => []
    , initState
    )

const None =
  Symbol ()

const relativePaths = (path = '.') =>
  readdirSync (path) .map (p => join (path, p))

const traverseDir = (dir) =>
  unfold
    ( (next, done, [ path = None, ...rest ]) =>
        path === None
          ? done ()
          : next ( path
                 , statSync (path) .isDirectory ()
                     ? relativePaths (path) .concat (rest)
                     : rest
                 )
    , relativePaths (dir)
    )

console.log (traverseDir ('.'))
// [ a, a/1, a/1/1, a/2, a/2/1, a/2/2, b, b/1, ... ]

如果這是你第一次看到這樣的節目, unfold會感到非常難以抗拒。 下面是一個用於生成小寫alphabet數組的unfold的簡化示例

 const unfold = (f, init) => f ( (x, next) => [ x, ...unfold (f, next) ] , () => [] , init ) const nextLetter = c => String.fromCharCode (c.charCodeAt (0) + 1) const alphabet = unfold ( (next, done, c) => c > 'z' ? done () : next ( c // value to add to output , nextLetter (c) // next state ) , 'a' // initial state ) console.log (alphabet) // [ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z ]

如果您仍然卡住,我在這里演示的技術將在類似問題的答案中更詳細地解釋

通常,最好使用fs模塊中的異步函數,因為這樣可以防止程序在磁盤讀取時間過長或網絡延遲的情況下掛起。 正如其他問答中所展示的那樣,展開與異步配合得很好

我正在使用以下getFilesTree函數。 此函數遞歸列出目錄及其子目錄中的所有文件,隱藏文件夾和文件除外(以.開頭)。

import {readdir} from 'node:fs/promises';
import {join, resolve} from 'node:path';
import {parse} from 'node:path';

export async function getFilesTree(dir) {
    return await Promise.all(
        (await readdir(dir, {withFileTypes: true}))
            .filter(child => !child.name.startsWith('.')) // skip hidden
            .map(async (child) => {
                const base = parse(child.name).base;
                const path = resolve(dir, child.name);
                return child.isDirectory() ?
                    {base, path, children: await getFilesTree(join(dir, child.name))} :
                    {base, path};
            }),
    );
}

函數本身與recursive-readdir庫非常相似。 結果看起來像這樣:

[
    {
        "base": "file.js",
        "path": "/Volumes/Work/file.js"
    },
    {
        "base": "css",
        "path": "/Volumes/Work/css",
        "children": [
            {
                "base": "index.css",
                "path": "/Volumes/Work/css/index.css"
            },
            {
                "base": "code.css",
                "path": "/Volumes/Work/css/code.css"
            }
        ]
    }
]

有時不需要結構化數據,那么您可以使用生成器代替:

import {readdir} from 'node:fs/promises';
import {resolve} from 'node:path';

async function * getFiles(dir) {
    for (const dirent of await readdir(dir, {withFileTypes: true})) {
        const res = resolve(dir, dirent.name);
        if (dirent.isDirectory()) {
            yield * getFiles(res);
        } else {
            yield res;
        }
    }
}

for await (const file of getFiles('content')) {
    console.log(file);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM