作者选择了 COVID-19 Relief Fund 作为 Write for DOnations 计划的一部分来接受捐赠。
介绍
使用文件对于开发目的和非开发目的一样普遍。 在日常计算机使用中,用户可能会在各种目录中的文件中读取和写入数据,以完成诸如保存下载的文件或访问要在另一个应用程序中使用的数据之类的任务。 同样,后端程序或命令行界面 (CLI) 工具可能需要将下载的数据写入文件才能保存,或者数据密集型应用程序可能需要导出到 JSON[X202X ]、CSV 或 Excel 格式。 这些程序需要与运行它们的操作系统的文件系统进行通信。
使用 Node.js,您可以使用内置的 fs 模块 以编程方式操作文件。 该名称是“文件系统”的缩写,该模块包含您在本地计算机上读取、写入和删除文件所需的所有功能。 Node.js 的这一独特方面使 JavaScript 成为后端和 CLI 工具编程的有用语言。
在本文中,您将使用 fs
模块读取通过命令行创建的文件,创建并写入新文件,删除您创建的文件,并将第一个文件移动到不同的文件夹中。 fs
模块支持同步、异步或通过流与文件交互; 本教程将重点介绍如何使用基于 Promise 的异步 API,这是 Node.js 开发人员最常用的方法。
先决条件
- 您必须在计算机上安装 Node.js 才能访问
fs
模块并按照教程进行操作。 本教程使用 Node.js 版本 10.22.0。 要在 macOS 或 Ubuntu 18.04 上安装 Node.js,请按照 如何在 macOS 上安装 Node.js 和创建本地开发环境或 使用 PPA 安装 部分中的步骤进行操作。 X199X]如何在 Ubuntu 18.04 上安装 Node.js。 - 本文使用 JavaScript Promises 处理文件,特别是使用
async/await
语法。 如果您不熟悉 Promises、async/await
语法或异步编程,请查看我们的指南 How To Write Asynchronous Code in Node.js。
第 1 步 — 使用 readFile()
读取文件
在这一步中,您将编写一个程序来读取 Node.js 中的文件。 为此,您需要导入 fs
模块,这是一个用于处理文件的标准 Node.js 模块,然后使用该模块的 readFile()
函数。 您的程序将读取文件,将其内容存储在变量中,然后将其内容记录到控制台。
第一步是为此活动和后面部分中的活动设置编码环境。
创建一个文件夹来存储您的代码。 在您的终端中,创建一个名为 node-files
的文件夹:
mkdir node-files
使用 cd
命令将工作目录更改为新创建的文件夹:
cd node-files
在此文件夹中,您将创建两个文件。 第一个文件将是一个新文件,其中包含您的程序稍后将读取的内容。 第二个文件将是读取该文件的 Node.js 模块。
使用以下命令创建文件 greetings.txt
:
echo "hello, hola, bonjour, hallo" > greetings.txt
echo
命令将其 string 参数打印到终端。 您使用 >
将 重定向 echo
的输出到新文件 greetings.txt
。
现在,在您选择的文本编辑器中创建并打开 readFile.js
。 本教程使用终端文本编辑器 nano
。 您可以使用 nano
打开此文件,如下所示:
nano readFile.js
该文件的代码可以分为三个部分。 首先,您需要导入 Node.js 模块,它允许您的程序处理文件。 在您的文本编辑器中,输入以下代码:
节点文件/readFile.js
const fs = require('fs').promises;
如前所述,您使用 fs
模块与文件系统进行交互。 但是请注意,您正在导入模块的 .promises
部分。
在第一次创建 fs
模块时,在 Node.js 中编写异步代码的主要方法是通过 callbacks。 随着 Promise 越来越受欢迎,Node.js 团队努力在开箱即用的 fs
模块中支持它们。 在 Node.js 版本 10 中,他们在使用 Promise 的 fs
模块中创建了一个 promises
对象,而主 fs
模块继续公开使用回调的函数。 在这个程序中,您正在导入模块的承诺版本。
导入模块后,您可以创建一个异步函数来读取文件。 异步函数以 async
关键字开头。 使用异步函数,您可以使用 await
关键字解析承诺,而不是使用 .then()
方法链接承诺。
创建一个新函数 readFile()
,它接受一个参数,一个名为 filePath
的字符串。 您的 readFile()
函数将使用 fs
模块将文件加载到使用 async/await
语法的变量中。
输入以下突出显示的代码:
节点文件/readFile.js
const fs = require('fs').promises; async function readFile(filePath) { try { const data = await fs.readFile(filePath); console.log(data.toString()); } catch (error) { console.error(`Got an error trying to read the file: ${error.message}`); } }
您使用 async
关键字定义函数,以便以后可以使用随附的 await
关键字。 要捕获异步文件读取操作中的错误,请将对 fs.readFile()
的调用与 try...catch 块 括起来。 在 try
部分中,您可以使用 fs.readFile()
函数将文件加载到 data
变量中。 该函数唯一需要的参数是文件路径,它以字符串形式给出。
fs.readFile()
默认返回一个 buffer 对象。 buffer
对象可以存储任何类型的文件类型。 当您记录文件的内容时,您可以使用缓冲区对象的 toString()
方法将这些字节转换为文本。
如果发现错误,通常是找不到文件或程序没有读取文件的权限,您会在控制台中记录收到的错误。
最后,使用以下突出显示的行调用 greetings.txt
文件上的函数:
节点文件/readFile.js
const fs = require('fs').promises; async function readFile(filePath) { try { const data = await fs.readFile(filePath); console.log(data.toString()); } catch (error) { console.error(`Got an error trying to read the file: ${error.message}`); } } readFile('greetings.txt');
请务必保存您的内容。 使用nano
,可以按CTRL+X
保存退出。
您的程序现在将读取您之前创建的 greetings.txt
文件并将其内容记录到终端。 通过使用 node
执行您的模块来确认这一点:
node readFile.js
您将收到以下输出:
Outputhello, hola, bonjour, hallo
您现在已经使用 async/await
语法读取了带有 fs
模块的 readFile()
函数的文件。
注意:在一些较早的Node.js版本中,使用fs
模块时会收到如下警告:
(node:13085) ExperimentalWarning: The fs.promises API is experimental
fs
模块的 promises
对象是在 Node.js 版本 10 中引入的,因此一些早期版本仍然称该模块为实验性的。 当 API 在版本 12.6 中变得稳定时,此警告已被删除。
现在您已经使用 fs
模块读取了一个文件,接下来您将创建一个文件并向其写入文本。
第 2 步 — 使用 writeFile()
写入文件
在此步骤中,您将使用 fs
模块的 writeFile()
功能编写文件。 您将在 Node.js 中创建一个 CSV 文件来跟踪杂货账单。 第一次编写文件时,您将创建文件并添加标题。 第二次,您会将数据附加到文件中。
在文本编辑器中打开一个新文件:
nano writeFile.js
通过导入 fs
模块开始您的代码:
节点文件/writeFile.js
const fs = require('fs').promises;
在创建两个函数时,您将继续使用 async/await
语法。 第一个功能是制作 CSV 文件。 第二个功能是将数据添加到 CSV 文件。
在您的文本编辑器中,输入以下突出显示的代码:
节点文件/writeFile.js
const fs = require('fs').promises; async function openFile() { try { const csvHeaders = 'name,quantity,price' await fs.writeFile('groceries.csv', csvHeaders); } catch (error) { console.error(`Got an error trying to write to a file: ${error.message}`); } }
此异步函数首先创建一个 csvHeaders
变量,其中包含 CSV 文件的列标题。 然后使用 fs
模块的 writeFile()
函数来创建一个文件并向其写入数据。 第一个参数是文件路径。 由于您只提供了文件名,Node.js 将在您正在执行代码的同一目录中创建该文件。 第二个参数是您正在写入的数据,在本例中为 csvHeaders
变量。
接下来,创建一个新函数以将项目添加到您的购物清单。 在文本编辑器中添加以下突出显示的函数:
节点文件/writeFile.js
const fs = require('fs').promises; async function openFile() { try { const csvHeaders = 'name,quantity,price' await fs.writeFile('groceries.csv', csvHeaders); } catch (error) { console.error(`Got an error trying to write to a file: ${error.message}`); } } async function addGroceryItem(name, quantity, price) { try { const csvLine = `\n${name},${quantity},${price}` await fs.writeFile('groceries.csv', csvLine, { flag: 'a' }); } catch (error) { console.error(`Got an error trying to write to a file: ${error.message}`); } }
异步 addGroceryItem()
函数接受三个参数:杂货商品的名称、您要购买的数量和单位价格。 这些参数与 模板文字语法 一起使用以形成 csvLine
变量,这是您正在写入文件的数据。
然后像在 openFile()
函数中那样使用 writeFile()
方法。 但是,这一次您有第三个参数:JavaScript 对象。 该对象有一个 flag
键,其值为 a
。 标志告诉 Node.js 如何与系统上的文件进行交互。 通过使用标志 a
,您是在告诉 Node.js 附加到文件,而不是覆盖它。 如果不指定标志,则默认为 w
,如果不存在则创建一个新文件,如果该文件已存在则覆盖该文件。 您可以在 Node.js 文档 中了解有关文件系统标志的更多信息。
要完成您的脚本,请使用这些函数。 在文件末尾添加以下突出显示的行:
节点文件/writeFile.js
... async function addGroceryItem(name, quantity, price) { try { const csvLine = `\n${name},${quantity},${price}` await fs.writeFile('groceries.csv', csvLine, { flag: 'a' }); } catch (error) { console.error(`Got an error trying to write to a file: ${error.message}`); } } (async function () { await openFile(); await addGroceryItem('eggs', 12, 1.50); await addGroceryItem('nutella', 1, 4); })();
要调用这些函数,首先使用 async function
创建一个包装函数。 由于在撰写本教程时无法在全局范围内使用 await
关键字,因此您必须将异步函数包装在 async function
中。 请注意,此函数是匿名的,这意味着它没有名称来标识它。
您的 openFile()
和 addGroceryItem()
函数是异步函数。 如果不将这些调用包含在另一个函数中,则无法保证内容的顺序。 您创建的包装器是使用 async
关键字定义的。 在该函数中,您使用 await
关键字对函数调用进行排序。
最后,async function
定义用括号括起来。 这些告诉 JavaScript 里面的代码是一个函数表达式。 函数末尾和分号之前的括号用于立即调用函数。 这称为 立即调用函数表达式 (IIFE)。 通过使用带有匿名函数的 IIFE,您可以测试您的代码是否生成包含三行的 CSV 文件:列标题、eggs
行和 nutella
的最后一行。
使用 CTRL+X
保存并退出 nano
。
现在,使用 node
命令运行您的代码:
node writeFile.js
不会有输出。 但是,您的当前目录中将存在一个新文件。
使用 cat
命令显示 groceries.csv
的内容:
cat groceries.csv
您将收到以下输出:
节点文件/groceries.csv
name,quantity,price eggs,12,1.5 nutella,1,4
您对 openFile()
的调用创建了一个新文件并为您的 CSV 添加了列标题。 随后对 addGroceryItem()
的调用然后添加了您的两行数据。
使用 writeFile()
功能,您可以创建和编辑文件。 接下来,您将删除文件,这是您有临时文件或需要在硬盘驱动器上腾出空间时的常见操作。
第 3 步 — 使用 unlink()
删除文件
在此步骤中,您将使用 fs
模块中的 unlink()
功能删除文件。 您将编写一个 Node.js 脚本来删除您在上一节中创建的 groceries.csv
文件。
在您的终端中,为此 Node.js 模块创建一个新文件:
nano deleteFile.js
现在您将编写创建异步 deleteFile()
函数的代码。 该函数将接受文件路径作为参数,将其传递给 unlink()
函数以将其从文件系统中删除。
在您的文本编辑器中,编写以下代码:
节点文件/deleteFile.js
const fs = require('fs').promises; async function deleteFile(filePath) { try { await fs.unlink(filePath); console.log(`Deleted ${filePath}`); } catch (error) { console.error(`Got an error trying to delete the file: ${error.message}`); } } deleteFile('groceries.csv');
unlink()
函数接受一个参数:要删除的文件的文件路径。
警告: 当您使用 unlink()
功能删除文件时,它不会发送到您的回收站或垃圾箱,而是从您的文件系统中永久删除。 此操作不可逆,因此请在执行代码之前确定您要删除该文件。
退出 nano
,确保通过输入 CTRL+X
保存文件的内容。
现在,执行程序。 在终端中运行以下命令:
node deleteFile.js
您将收到以下输出:
OutputDeleted groceries.csv
要确认文件不再存在,请在当前目录中使用 ls
命令:
ls
此命令将显示这些文件:
OutputdeleteFile.js greetings.txt readFile.js writeFile.js
您现在已确认您的文件已使用 unlink()
功能删除。
到目前为止,您已经学习了如何读取、写入、编辑和删除文件。 以下部分使用一个函数将文件移动到不同的文件夹。 学习该功能后,您将能够在 Node.js 中完成最关键的文件管理任务。
第 4 步 — 使用 rename()
移动文件
文件夹用于组织文件,因此能够以编程方式将文件从一个文件夹移动到另一个文件夹使文件管理更容易。 您可以使用 rename() 函数在 Node.js 中移动文件。 在此步骤中,您会将 greetings.txt
文件的副本移动到新文件夹中。
在编写 Node.js 模块之前,您需要设置一些东西。 首先创建一个文件夹,您将把文件移动到该文件夹中。 在您的终端中,在您的当前目录中创建一个 test-data
文件夹:
mkdir test-data
现在,使用 cp
命令复制第一步中使用的 greetings.txt
文件:
cp greetings.txt greetings-2.txt
通过打开一个包含您的代码的 JavaScript 文件来完成设置:
nano moveFile.js
在您的 Node.js 模块中,您将创建一个名为 moveFile()
的函数,该函数调用 rename()
函数。 使用rename()
功能时,需要提供原始文件的文件路径和目标位置的路径。 对于此示例,您将使用 moveFile()
函数将 greetings-2.txt
文件移动到 test-data
文件夹中。 您还将其名称更改为 salutations.txt
。
在打开的文本编辑器中输入以下代码:
节点文件/moveFile.js
const fs = require('fs').promises; async function moveFile(source, destination) { try { await fs.rename(source, destination); console.log(`Moved file from ${source} to ${destination}`); } catch (error) { console.error(`Got an error trying to move the file: ${error.message}`); } } moveFile('greetings-2.txt', 'test-data/salutations.txt');
如前所述,rename()
函数有两个参数:源文件路径和目标文件路径。 此功能可以将文件移动到其他文件夹,重命名当前目录中的文件,或者同时移动和重命名。 在您的代码中,您正在移动和重命名文件。
按 CTRL+X
保存并退出 nano
。
接下来,用 node
执行这个程序。 输入以下命令运行程序:
node moveFile.js
您将收到以下输出:
OutputMoved file from greetings-2.txt to test-data/salutations.txt
要确认文件不再存在于当前目录中,可以使用 ls
命令:
ls
此命令将显示这些文件和文件夹:
OutputdeleteFile.js greetings.txt moveFile.js readFile.js test-data writeFile.js
您现在可以使用 ls
列出 test-data
子文件夹中的文件:
ls test-data
您移动的文件将出现在输出中:
Outputsalutations.txt
您现在已经使用 rename()
函数将文件从当前目录移动到子文件夹中。 您还使用相同的函数调用重命名了文件。
结论
在本文中,您学习了使用 Node.js 管理文件的各种功能。 您首先使用 readFile()
加载文件的内容。 然后,您使用 writeFile()
函数创建了新文件并将数据附加到现有文件中。 您使用 unlink()
函数永久删除了一个文件,然后使用 rename()
移动并重命名了一个文件。
以编程方式处理文件是 Node.js 的一项重要功能。 程序可能需要输出文件供用户使用,或者可能需要为不总是运行的应用程序存储数据。 通过 fs
模块的功能,开发人员可以控制文件在我们的 Node.js 程序中的使用方式。
要了解有关 fs
模块的更多信息,您可以阅读 Node.js 文档。 如果您想继续学习 Node.js,您可以返回 如何在 Node.js 中编写代码系列,或者在我们的 Node 主题页面 上浏览编程项目和设置。