练习 - 使用文件系统

已完成

Tailwind Traders 在世界各地有许多实体商店。 每天夜晚,这些存储会创建一个名为“sales.json”的文件,其中包含前一天所有销售的总额。 这些文件在按商店 ID 命名的文件夹中进行组织。

在本练习中,你将编写一个 Node.js 程序,该程序可在文件夹中搜索名为“sales.json”的文件。

在开发容器中打开项目

  1. 开始在 MicrosoftDocs/node-essentials GitHub 存储库的 main 分支上创建新的 GitHub Codespace。

  2. 在“创建 codespace”页上,查看 codespace 配置设置,然后选择“新建 codespace

    Screenshot of the confirmation screen before creating a new codespace.

  3. 等待 Codespace 启动。 此启动过程会花费几分钟时间。

  4. 在 codespace 中打开新终端。

    提示

    可以使用主菜单导航到“终端”菜单选项,然后选择“新建终端”选项。

    Screenshot of the codespaces menu option to open a new terminal.

  5. 验证环境中是否安装了 Node.js:

    node --version
    

    开发容器使用 Node.js LTS 版本,例如 v20.5.1。 确切的版本可能有所不同。

  6. 此项目中的剩余练习在此开发容器的上下文中进行。

查找 sales.json 文件

你的任务是找到 stores 文件夹中的所有文件。

展开“stores”文件夹和其中每个带编号的文件夹。

Screenshot that shows the project folder structure.

包含“fs”模块

  1. ./nodejs-files 子文件夹中,创建一个 index.js 文件,以便在编辑器中打开它。

  2. 在文件的顶部,添加以下代码以在文件中包括 fs 模块。

    const fs = require("fs").promises;
    
  3. 接下来,创建 main 函数,该函数是代码的入口点。 此文件中的最后一行代码调用 main 方法。

    const fs = require("fs").promises;
    
    async function main() {}
    
    main();
    

    这是典型的 CommonJS 样板代码,用于调用异步函数。

编写函数以查找 sales.json 文件

  1. 创建一个名为 findSalesFiles 的新函数,该函数采用 folderName 参数。

    async function findSalesFiles(folderName) {
      // FIND SALES FILES
    }
    
  2. findSalesFiles 函数中,添加以下代码以完成以下任务:

    • (1) 在顶部添加一个数组,用于保存程序找到的所有销售文件的路径。
    • (2) 使用 readdir 方法读取 currentFolder。
    • (3) 使用异步 for...of 循环添加一个块以循环访问从 readdir 方法返回的每个项。
    • (4) 添加 if 语句,以确定该项是文件还是目录。
    • (5) 如果项是目录,则再次递归调用函数 findSalesFiles,并传入项的路径。
    • (6) 如果不是目录,请添加检查以确保项名称与 sales.json 匹配。
    async function findSalesFiles(folderName) {
    
       // (1) Add an array at the top, to hold the paths to all the sales files that the program finds.
       let results = [];
    
       // (2) Read the currentFolder with the `readdir` method. 
       const items = await fs.readdir(folderName, { withFileTypes: true });
    
       // (3) Add a block to loop over each item returned from the `readdir` method using the asynchronous `for...of` loop. 
       for (const item of items) {
    
         // (4) Add an `if` statement to determine if the item is a file or a directory. 
         if (item.isDirectory()) {
    
           // (5) If the item is a directory, recursively call the function `findSalesFiles` again, passing in the path to the item. 
           const resultsReturned = await findSalesFiles(`${folderName}/${item.name}`);
           results = results.concat(resultsReturned);
         } else {
           // (6) If it's not a directory, add a check to make sure the item name matches *sales.json*.
           if (item.name === "sales.json") {
             results.push(`${folderName}/${item.name}`);
           }
         }
       }
    
       return results;
    }
    
  3. main 方法调用此新的 findSaleFiles 函数。 传入“stores”文件夹名称作为文件的搜索位置。

     async function main() {
       const results = await findSalesFiles("stores");
       console.log(results);
     }
    
  4. 完整的应用程序如下所示:

    const fs = require("fs").promises;
    
    async function findSalesFiles(folderName) {
    
      // (1) Add an array at the top, to hold the paths to all the sales files that the program finds.
      let results = [];
    
      // (2) Read the currentFolder with the `readdir` method. 
      const items = await fs.readdir(folderName, { withFileTypes: true });
    
      // (3) Add a block to loop over each item returned from the `readdir` method using the asynchronous `for...of` loop. 
      for (const item of items) {
    
        // (4) Add an `if` statement to determine if the item is a file or a directory. 
        if (item.isDirectory()) {
    
          // (5) If the item is a directory, recursively call the function `findSalesFiles` again, passing in the path to the item. 
          const resultsReturned = await findSalesFiles(`${folderName}/${item.name}`);
          results = results.concat(resultsReturned);
        } else {
          // (6) If it's not a directory, add a check to make sure the item name matches *sales.json*.
          if (item.name === "sales.json") {
            results.push(`${folderName}/${item.name}`);
          }
        }
      }
    
      return results;
    }
    
    async function main() {
      const results = await findSalesFiles("stores");
      console.log(results);
    }
    
    main();
    

运行程序

  1. 在终端输入以下命令以运行程序。

    node index.js
    
  2. 程序应显示以下输出。

    [
     'stores/201/sales.json',
     'stores/202/sales.json',
     'stores/203/sales.json',
     'stores/204/sales.json',
    ]
    

很好! 你已成功编写了一个命令行工具,该工具可以遍历所有目录并在内部查找所有“sales.json”文件。

但是,在此示例中构造子文件夹路径的方式有点笨拙,因为它需要将字符串串联在一起。 此外,你可能会因为使用采用其他路径分隔符的其他操作系统(如 Windows)而遇到问题。

在下一节中,你将学习如何使用“path”模块构造在操作系统上使用的路径。

遇到困难了?

如果在此练习中遇到任何问题,可参考下面的完整代码。 删除 index.js 中的所有内容,并将其替换为此解决方案。

const fs = require("fs").promises;

async function findSalesFiles(folderName) {

  // (1) Add an array at the top, to hold the paths to all the sales files that the program finds.
  let results = [];

  // (2) Read the currentFolder with the `readdir` method. 
  const items = await fs.readdir(folderName, { withFileTypes: true });

  // (3) Add a block to loop over each item returned from the `readdir` method using the asynchronous `for...of` loop. 
  for (const item of items) {

    // (4) Add an `if` statement to determine if the item is a file or a directory. 
    if (item.isDirectory()) {

      // (5) If the item is a directory, recursively call the function `findSalesFiles` again, passing in the path to the item. 
      const resultsReturned = await findSalesFiles(`${folderName}/${item.name}`);
      results = results.concat(resultsReturned);
    } else {
      // (6) If it's not a directory, add a check to make sure the item name matches *sales.json*.
      if (item.name === "sales.json") {
        results.push(`${folderName}/${item.name}`);
      }
    }
  }

  return results;
}

async function main() {
  const results = await findSalesFiles("stores");
  console.log(results);
}

main();