如何在遍历一系列目录的循环中使用异步 readdir 函数?

Posted

技术标签:

【中文标题】如何在遍历一系列目录的循环中使用异步 readdir 函数?【英文标题】:How to use async readdir function inside a loop iterating over series of directories? 【发布时间】:2021-01-13 09:46:16 【问题描述】:

我正在归档来自多个目录的某些文本文件。因此,首先,我在一个文件夹中进行迭代,该文件夹为我提供了 folderPath,这个 folderPath 可以包含许多文件(txt、pdf 等)并且有多个文件夹路径,我对 folderPath 使用异步 readdir 并将这些单独的文件附加到归档器,然后最后关闭。如果我在文件夹循环结束之前执行 archive.finalize ,它不会在 zip 中生成所需数量的 txt 文件,而只是生成初始文件,这是显而易见的。如果我将 archive.finalize 保留在第 2 行,则会向我抛出一个错误,如下所示的目录结构。有人可以在这方面提供帮助吗?

目录结构如下:

主文件夹/文件夹1 主文件夹/文件夹2

mainfolder/folder1/sometext.txt mainfolder/folder1/someanothertext.txt

mainfolder/folder2/sometext.txt mainfolder/folder2/someanothertext.txt

现在我想把它压缩成:

Outuput.zip,其中包含 -> folder1 和 folder2 以及各自的 txt 文件。当使用同步功能到readdir(readdirsync)时,我能够实现它, 但是使用异步,我面临一些回调问题。

错误:

ArchiverError: queue closed
    at Archiver.file (C:\Users\Workspace\code\node_modules\archiver\lib\core.js:692:24)
    at C:\Users\Workspace\current_code_snippet
    at Array.forEach (<anonymous>)
    at C:\Users\current_code_snippet_ line 3 as specified in code snippet
    at FSReqCallback.oncomplete (fs.js:156:23) 
  code: 'QUEUECLOSED',
  data: undefined

代码:

this.children.forEach((value, _index, _array) => 
    const folderPath = path.join(basePath, value);
    fs.readdir(folderPath, (err, fileNames) => 
          if (err)
              throw err;
          
          else 
            fileNames.forEach(file =>   // line 3
              if (outputType === ".txt") 
                  const filePath = path.join(basePath, value, file);
                  archive.file(filePath,  name: `$value/$file` ); // file is saved as value/file inside parent zip
              
            )
          
    )
    archive.finalize(); // line 1
);
archive.finalize(); // line 2

【问题讨论】:

看来archive.finalize() 调用得太早了。 archive.file 是异步操作吗? 好像不是。你必须向上移动archive.finalizefs.readdir 回调。 (我完全不明白为什么你有// line 2)因为在添加任何文件之前调用了archive.finalize,这似乎是在指出数据未定义的错误中 但是移动它并不会遍历所有目录,只会归档一些文件。我只有在里面(第 1 行)-> 这给了我一个 zip 文件,但不是所有文件都在里面。当我使用 readdirSync 时,它在第 2 行工作 是的,这是有道理的。一旦你在你的 foreach 循环中开始一个异步操作,archive.finalize 函数就会被调用 【参考方案1】:

我会将 fs 调用包装到 Promise 中。这使得等待操作成为可能,并且降低了代码的一些复杂性。

注意forEach 循环不适用于async, await

const children = ["path_0", "path_1", "path_2", "path_3"];

// mock fs / async action
const fs = 
    readdir: (path, error, success) => 
        setTimeout(() => 
            success(["file_0", "file_1", "file_2"]);
        , Math.random() * 1000);
    


// function I would add to make the process simpler
const readFiles = (path) => 
    return new Promise(res => 
        fs.readdir(path, () => , (s) => 
            res(s);
        );
    );

// start helper as async
const start = async() => 
    // first await all files to be added to the archive
    await Promise.all(children.map(async child => 
        const files = await readFiles();
        // loop to add all files to the zip
        // archive.file(filePath,  name: `$value/$file` );
        console.log(child, files);
    ));
    // then archive all files
    // archive.finalize();
    console.log("finalize archive");

start();

【讨论】:

以上是关于如何在遍历一系列目录的循环中使用异步 readdir 函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何以角度循环遍历异步数据

如何在单击多个项目时使用 jQuery 循环遍历数组?

遍历 ksh 中的一系列整数?

Array中使用异步函数遍历元素

在 HTML5 的 SQLite 查询中循环遍历多个表 - 异步计时

如何循环遍历 Perl 目录中的文件? [复制]