作者选择了COVID-19 救济基金来接受捐赠,作为Write for DOnations计划的一部分。
介绍
处理文件对于开发目的和非开发目的一样普遍。在日常计算机使用中,用户可能会在各种目录中的文件中读取和写入数据,以完成诸如保存下载的文件或访问要在其他应用程序中使用的数据等任务。同样,后端程序或命令行界面 (CLI) 工具可能需要将下载的数据写入文件以进行保存,或者数据密集型应用程序可能需要导出为JSON、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 和创建本地开发环境或如何在 Ubuntu 18.04 上安装 Node.js 的使用 PPA 安装部分中的步骤进行操作。 -
本文使用 JavaScript Promise 来处理文件,尤其是
async/await
语法。如果您不熟悉 Promise、async/await
语法或异步编程,请查看我们关于如何在 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
命令将其字符串参数打印到终端。您可以使用>
到重定向 echo
的输出到一个新的文件,greetings.txt
。
现在,readFile.js
在您选择的文本编辑器中创建并打开。本教程使用nano
,一个终端文本编辑器。你可以这样打开这个文件nano
:
- nano readFile.js
该文件的代码可以分为三个部分。首先,您需要导入允许您的程序处理文件的Node.js 模块。在您的文本编辑器中,输入以下代码:
const fs = require('fs').promises;
如前所述,您使用该fs
模块与文件系统进行交互。但是请注意,您正在导入.promises
模块的一部分。
当fs
首次创建模块,写在Node.js的异步代码的主要方式是通过回调。随着 Promise 越来越流行,Node.js 团队致力于在fs
开箱即用的模块中支持它们。在 Node.js 版本 10 中,他们在使用 Promisepromises
的fs
模块中创建了一个对象,而主fs
模块继续公开使用回调的函数。在此程序中,您将导入模块的 promise 版本。
导入模块后,您可以创建一个异步函数来读取文件。异步函数以async
关键字开头。使用异步函数,您可以使用await
关键字解析承诺,而不是将承诺与.then()
方法链接起来。
创建一个readFile()
接受一个参数的新函数,一个名为 的字符串filePath
。您的readFile()
函数将使用fs
模块使用async/await
语法将文件加载到变量中。
输入以下突出显示的代码:
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
block将调用括起来。在该try
部分中,您data
使用该fs.readFile()
函数将文件加载到变量中。该函数唯一需要的参数是文件路径,它以字符串形式给出。
在fs.readFile()
返回buffer
的默认对象。一个buffer
对象可以存储任何类型的文件。当您记录文件的内容时,您可以使用toString()
缓冲区对象的方法将这些字节转换为文本。
如果捕获到错误,通常是未找到文件或程序没有读取文件的权限,则会在控制台中记录收到的错误。
最后,greetings.txt
使用以下突出显示的行调用文件上的函数:
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
您现在已经使用语法读取了包含fs
模块readFile()
函数的文件async/await
。
注意:在一些较早版本的 Node.js 中,使用fs
模块时会收到以下警告:
(node:13085) ExperimentalWarning: The fs.promises API is experimental
模块的promises
对象fs
是在 Node.js 版本 10 中引入的,所以一些早期版本仍然称模块为实验性的。当 API 在版本 12.6 中变得稳定时,此警告被删除。
现在您已经使用fs
模块读取了一个文件,接下来您将创建一个文件并向其中写入文本。
第 2 步 — 用 writeFile()
在这一步中,您将编写具有模块writeFile()
功能的文件fs
。您将在 Node.js 中创建一个 CSV 文件来跟踪杂货账单。第一次写入文件时,您将创建文件并添加标题。第二次,您将数据附加到文件中。
在文本编辑器中打开一个新文件:
- nano writeFile.js
通过导入fs
模块开始您的代码:
const fs = require('fs').promises;
async/await
在创建两个函数时,您将继续使用语法。第一个功能是制作 CSV 文件。第二个功能是将数据添加到 CSV 文件。
在文本编辑器中,输入以下突出显示的代码:
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 文件列标题的变量。然后使用该模块的writeFile()
功能fs
创建一个文件并向其中写入数据。第一个参数是文件路径。由于您只提供了文件名,Node.js 将在您执行代码的同一目录中创建文件。第二个参数是您正在写入的数据,在本例中为csvHeaders
变量。
接下来,创建一个新函数以将项目添加到您的购物清单。在文本编辑器中添加以下突出显示的函数:
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
变量,即您要写入文件的数据。
然后writeFile()
像在openFile()
函数中一样使用该方法。然而,这次你有第三个参数:一个JavaScript 对象。这个对象有一个flag
值为的键a
。标志告诉 Node.js 如何与系统上的文件交互。通过使用 flag a
,您是在告诉 Node.js 附加到文件中,而不是覆盖它。如果您不指定标志,则默认为w
,如果不存在则创建一个新文件,如果文件已存在则覆盖该文件。您可以在Node.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
。
保存并退出nano
用CTRL+X
。
现在,使用以下node
命令运行您的代码:
- node writeFile.js
不会有输出。但是,您的当前目录中将存在一个新文件。
使用cat
命令显示内容groceries.csv
:
- cat groceries.csv
您将收到以下输出:
name,quantity,price
eggs,12,1.5
nutella,1,4
您的调用openFile()
创建了一个新文件并为您的 CSV 添加了列标题。随后的调用addGroceryItem()
添加了您的两行数据。
使用该writeFile()
功能,您可以创建和编辑文件。接下来,您将删除文件,这是当您有临时文件或需要在硬盘驱动器上腾出空间时的常见操作。
步骤 3 — 删除文件 unlink()
在这一步中,您将删除模块中具有该unlink()
功能的文件fs
。您将编写一个 Node.js 脚本来删除groceries.csv
您在上一节中创建的文件。
在您的终端中,为此 Node.js 模块创建一个新文件:
- nano deleteFile.js
现在您将编写创建异步deleteFile()
函数的代码。该函数将接受一个文件路径作为参数,将其传递给unlink()
函数以将其从文件系统中删除。
在您的文本编辑器中,编写以下代码:
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
现在,greetings.txt
使用以下cp
命令复制第一步中使用的文件:
- cp greetings.txt greetings-2.txt
通过打开 JavaScript 文件来包含您的代码来完成设置:
- nano moveFile.js
在您的 Node.js 模块中,您将创建一个moveFile()
调用该rename()
函数的函数。使用该rename()
功能时,需要提供原文件的文件路径和目标位置的路径。对于此示例,您将使用moveFile()
函数将greetings-2.txt
文件移动到文件test-data
夹中。您还将其名称更改为salutations.txt
.
在打开的文本编辑器中输入以下代码:
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()
函数采用两个参数:源文件路径和目标文件路径。此功能可以将文件移动到其他文件夹,重命名当前目录中的文件,或同时移动和重命名。在您的代码中,您正在移动和重命名您的文件。
nano
按保存并退出CTRL+X
。
接下来,使用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 主题页面浏览编程项目和设置。