如何在 Node.js 中使用文件系统

前言:Web 应用程序并不总是需要写入文件系统,但 Node.js 提供了一个全面的应用程序编程接口 (API) 来实现这一点。如果您要输出调试日志、将文件传输到服务器或从服务器传输文件,或者创建命令行工具,那么它可能是必不可少的。

值得注意的是:

1、Windows、macOS 和 Linux 处理文件的方式不同。例如,您使用正斜杠 / 来分隔 macOS 和 Linux 中的目录,但 Windows 使用反斜杠 \ 并禁止使用某些文件名字符,例如 : 和 ?。

2、检查权限。用户或其他应用程序可能会删除文件或更改访问权限。始终检查此类问题并有效处理错误。

Node.js fs 模块

Node.js fs 模块提供管理文件和目录的方法。如果您正在使用其他 JavaScript 运行时:

Deno 提供自己的文件系统 API 以及对 node:fs API 的支持。

Bun 提供优化的文件 I/O API 以及 node:fs API。

浏览器在沙盒中运行,无法直接与操作系统或底层文件系统通信。也就是说,您可以通过文件系统 API 上传文件并允许有限的访问。它在概念上有所不同,超出了本教程的范围。

所有 JavaScript 运行时都在单个处理线程上运行。底层操作系统处理文件读写等操作,因此 JavaScript 程序继续并行运行。然后,操作系统会在文件操作完成时提醒运行时。

fs 文档提供了一长串函数,但有三种具有类似功能的一般类型,我们将在下文中介绍。

1. 回调函数

这些函数以完成回调函数作为参数。以下示例传递一个内联函数,该函数输出 myfile.txt 的内容。假设没有错误,则其内容在程序结束时显示在控制台中:

import { readFile } from 'node:fs';readFile('myfile.txt', { encoding: 'utf8' }, (err, content) => {if (!err) {console.log(content);}
});console.log('end of program');

注意:{ encoding: 'utf8' } 参数确保 Node.js 返回文本内容的字符串,而不是二进制数据的 Buffer 对象。

当您需要一个接一个地运行并陷入嵌套回调地狱时,这会变得复杂!编写回调函数也很容易,这些回调函数看起来正确,但会导致难以调试的内存泄漏。

在大多数情况下,今天几乎没有理由使用回调。下面的几个示例都使用了它们。

2.同步函数

“Sync” 函数实际上忽略了 Node 的非阻塞 I/O,并提供了与其他编程语言类似的同步 API。以下示例在控制台中出现程序结束之前输出 myfile.txt 的内容:

import { readFileSync } from 'node:fs';try {const content = readFileSync('myfile.txt', { encoding: 'utf8' });console.log(content);
}
catch {}console.log('end of program');

它看起来更容易,我永远不会说不要使用 Sync……但是,呃……不要使用 Sync!它会停止事件循环并暂停您的应用程序。在 CLI 程序中加载小型初始化文件时,这可能没问题,但请考虑一个有 100 个并发用户的 Node.js Web 应用程序。如果一个用户请求一个需要一秒钟才能加载的文件,他们会等待一秒钟才能得到响应——其他 99 个用户也是如此!

3. Promise 函数

ES6/2015 引入了 Promise。它们是回调的语法糖,可提供更甜蜜、更简单的语法,尤其是与 async/await 一起使用时。Node.js 还引入了“fs/promises”API,其外观和行为与同步函数语法类似,但仍然是异步的:

import { readFile } from 'node:fs/promises';try {const content = await readFile('myfile.txt', { encoding: 'utf8' });console.log(content);
}
catch {}console.log('程序结束');

请注意使用 'node:fs/promises' 模块和 readFile() 之前的 await。

下面的大多数示例都使用基于承诺的语法。大多数示例为简洁起见不包括 try 和 catch,但您应该添加这些块来处理错误。

ES 模块语法

本教程中的示例还使用 ES 模块 (ESM) 导入,而不是 CommonJS 要求。ESM 是 Deno、Bun 和浏览器运行时支持的标准模块语法。

要在 Node.js 中使用 ESM,请执行以下操作之一:

1、使用 .mjs 扩展名命名 JavaScript 文件
2、在命令行上使用 --import=module  例如 node --import=module index.js,或者
3、如果您有项目 package.json 文件,请添加新的“type”:“module”设置
如果需要,您仍然可以使用 CommonJS 要求。

读取文件

 有几种读取文件的函数,但最简单的方法是使用 readFile 将整个文件读入内存,如上例所示:

import { readFile } from 'node:fs/promises';
const content = await readFile('myfile.txt', { encoding: 'utf8' });

第二个选项对象也可以是字符串。它定义编码:设置“utf8”或其他文本格式以将文件内容读入字符串。

或者,您可以使用 filehandle 对象的 readLines() 方法一次读取一行:

import { open } from 'node:fs/promises';const file = await open('myfile.txt');for await (const line of file.readLines()) {console.log(line);
}

处理文件和目录路径


您经常需要访问特定绝对路径或相对于 Node 应用程序工作目录的路径的文件。node:path 模块提供了跨平台方法来解析所有操作系统上的路径。

path.sep 属性返回目录分隔符 — Windows 上的 \ 或 Linux 或 macOS 上的 /:

import * as path from 'node:path';console.log( path.sep );

但还有更多有用的属性和功能。join([…paths]) 连接所有路径段并针对操作系统进行规范化:

console.log( path.join('/project', 'node/example1', '../example2', 'myfile.txt') );
/*
/project/node/example2/myfile.txt on macOS/Linux
\project\node\example2\myfile.txt on Windows
*/

resolve([…paths]) 类似,但返回完整的绝对路径:

console.log( path.resolve('/project', 'node/example1', '../example2', 'myfile.txt') );
/*
/project/node/example2/myfile.txt on macOS/Linux
C:\project\node\example2\myfile.txt on Windows
*/

normalize(path) 解析所有目录 .. 和 . 引用:

console.log( path.normalize('/project/node/example1/../example2/myfile.txt') );
/*
/project/node/example2/myfile.txt on macOS/Linux
\project\node\example2\myfile.txt on Windows
*/

relative(from, to) 计算两个绝对路径或相对路径之间的相对路径(基于 Node 的工作目录):

console.log( path.relative('/project/node/example1', '/project/node/example2') );
/*
../example2 on macOS/Linux
..\example2 on Windows
*/

format(object) 从组成部分的对象构建完整路径:

console.log(path.format({dir: '/project/node/example2',name: 'myfile',ext: 'txt'})
);
/*
/project/node/example2/myfile.txt
*/

parse(path) 执行相反的操作并返回描述路径的对象:

console.log( path.parse('/project/node/example2/myfile.txt') );
/*
{root: '/',dir: '/project/node/example2',base: 'myfile.txt',ext: '.txt',name: 'myfile'
}
*/

获取文件和目录信息

您经常需要获取有关路径的信息。它是文件吗?它是目录吗?它是什么时候创建的?它上次修改的时间是什么时候?您能读取它吗?您能向其中附加数据吗?

stat(path) 函数返回一个 Stats 对象,其中包含有关文件或目录对象的信息:

import { stat } from 'node:fs/promises';const info = await stat('myfile.txt');
console.log(info);
/*
Stats {dev: 4238105234,mode: 33206,nlink: 1,uid: 0,gid: 0,rdev: 0,blksize: 4096,ino: 3377699720670299,size: 21,blocks: 0,atimeMs: 1700836734386.4246,mtimeMs: 1700836709109.3108,ctimeMs: 1700836709109.3108,birthtimeMs: 1700836699277.3362,atime: 2023-11-24T14:38:54.386Z,mtime: 2023-11-24T14:38:29.109Z,ctime: 2023-11-24T14:38:29.109Z,birthtime: 2023-11-24T14:38:19.277Z
}
*/

它还提供了的方法,包括:

const isFile = info.isFile(); // true
const isDirectory = info.isDirectory(); // false

access(path) 函数测试是否可以使用通过常量设置的特定模式访问文件。如果可访问性检查成功,则承诺将不产生任何值。如果失败,则承诺将被拒绝。例如:

import { access, constants } from 'node:fs/promises';const info = {canRead: false,canWrite: false,canExec: false
};// 可读吗?
try {await access('myfile.txt', constants.R_OK);info.canRead = true;
}
catch {}// 可写
try {await access('myfile.txt', constants.W_OK);info.canWrite = true;
}
catch {}console.log(info);
/*
{canRead: true,canWrite: true
}
*/

您可以测试多种模式,例如测试文件是否可读又可写:

写入文件

writeFile() 是最简单的函数,用于异步写入整个文件(如果文件已存在)并替换其内容:

import { writeFile } from 'node:fs/promises';
await writeFile('myfile.txt', 'new file contents');

传递以下参数:

1、文件路径

2、文件内容 — 可以是字符串、缓冲区、TypedArray、DataView、Iterable 或 Stream

3、可选的第三个参数可以是表示编码的字符串(例如“utf8”)或具有编码和中止承诺的信号等属性的对象。

类似的 appendFile() 函数将新内容添加到当前文件的末尾,如果该文件不存在,则创建该文件。

对于喜欢冒险的人来说,有一个文件处理程序 write() 方法,它允许您在特定点和长度替换文件内的内容。

创建目录

mkdir() 函数可以通过传递绝对或相对路径来创建完整的目录结构:

import { mkdir } from 'node:fs/promises';await mkdir('./subdir/temp', { recursive: true });

您可以传递两个参数:

1、目录路径
2、带有递归布尔值和模式字符串或整数的可选对象

将递归设置为 true 会创建整个目录结构。上面的示例在当前工作目录中创建 subdir 并将 temp 创建为其子目录。如果递归为 false(默认值),则如果 subdir 尚未定义,则承诺将拒绝。

模式是 macOS/Linux 用户、组和其他权限,默认值为 0x777。这在 Windows 上不受支持,并且会被忽略。

类似的 .mkdtemp() 函数类似,并创建一个通常用于临时数据存储的唯一目录。

读取目录内容

.readdir() 读取目录内容。承诺以包含所有文件和目录名称(除 . 和 .. 外)的数组实现。名称相对于目录,不包含完整路径:

import { readdir } from 'node:fs/promises';const files = await readdir('./'); // 当前工作目录
for (const file of files) {console.log(file);
}/*
file1.txt
file2.txt
file3.txt
index.mjs
*/

您可以传递具有以下属性的可选第二个参数对象:

1、encoding — 默认值为 utf8 字符串数组
2、recursive — 设置为 true 以递归方式从所有子目录中获取所有文件。文件名将包含子目录名称。旧版本的 Node.js 可能不提供此选项。
3、withFileType — 设置为 true 以返回 fs.Dirent 对象数组,其中包括 .name、.path、.isFile()、.isDirectory() 等属性和方法。
替代的 .opendir() 函数允许您异步打开目录进行迭代扫描:

import { opendir } from 'node:fs/promises';const dir = await opendir('./');
for await (const entry of dir) {console.log(entry.name);
}

删除文件和目录

.rm() 函数删除指定路径下的文件或目录:

import { rm } from 'node:fs/promises';await rm('./oldfile.txt');

您可以传递具有以下属性的可选第二个参数对象:

1、force — 设置为 true,当路径不存在时不会引发错误
2、recursive — 设置为 true,以递归方式删除目录和内容
3、maxRetries — 当另一个进程锁定文件时,进行多次重试
4、retryDelay — 重试之间的毫秒数


类似的 .rmdir() 函数仅删除目录(您不能传递文件路径)。同样,.unlink() 仅删除文件或符号链接(您不能传递目录路径)。

其他文件系统函数


上面的示例说明了读取、写入、更新和删除文件和目录的最常用选项。Node.js 还提供了其他较少使用的选项,例如复制、重命名、更改所有权、更改权限、更改日期属性、创建符号链接和监视文件更改。

监视文件更改时,最好使用基于回调的 API,因为它的代码更少、更易于使用,并且不会停止其他进程:

import { watch } from 'node:fs';// 当目录中有任何变化时运行回调
watch('./mydir', { recursive: true }, (event, file) => {console.log(`event: ${ event }`);if (file) {console.log(`file changed: ${ file }`);}});

回调接收的事件参数是“change”或“rename”。

摘要:Node.js 提供了灵活的跨平台 API,用于管理任何可以使用运行时的操作系统上的文件和目录。只要稍加注意,您就可以编写可以与任何文件系统交互的强大且可移植的 JavaScript 代码。

有关更多信息,请参阅 Node.js fs 和路径文档。其他有用的库包括:

1、OS 用于查询操作系统信息
2、URL 用于解析 URL,可能用于映射到文件系统路径和从文件系统路径映射 URL
3、Stream用于处理大文件
4、Buffer 和 TypedArray对象用于处理二进制数据
5、Child processes用于生成子进程来处理长时间运行或复杂的文件操作函数。
您还可以在 npm 上找到更高级别的文件系统模块,但没有比编写自己的模块更好的体验了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/35868.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ELK集群设置密码

一、软件安装清单 elasticsearch7.17.22logstash7.17.22kibana:7.17.22filebeat7.17.22elasticsearch-head:5 二、配置 生成证书 进入elasticsearch容器 bin/elasticsearch-certutil cert -out /usr/share/elasticsearch/config/elastic-certificates.p12 -pass将证书拷贝…

HTML DOM 修改 HTML 内容

HTML DOM 修改 HTML 内容 HTML DOM(文档对象模型)是 HTML 和 XML 文档的编程接口。它提供了对文档的结构化表示,并定义了一种方式来访问和操作文档的内容、结构和样式。在网页开发中,使用 HTML DOM 可以动态地修改 HTML 元素的内容、属性和样式。 基本概念 在 HTML DOM …

在Ubuntu上安装和配置配置服务器防火墙(CSF)的方法

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 Config Server Firewall(CSF)是大多数 Linux 发行版和基于 Linux 的 VPS 的免费高级防火墙。除了基本的防…

qt for android 工程添加AndroidManifest.xml 文件

1.选择左边图形栏目中的Projects,在Build steps下的Build Android APK中Details 2.点击Create Templates,并勾选 此时在工程下面会多出一个文件夹android 3.将这个android的中所有文件加入工程中,编辑.pro 4.通过QT 图形化编辑设置属性&#…

JAVA【案例5-5】二月天

【二月天】 1、案例描述 二月是一个有趣的月份,平年的二月有28天,闰年的二月由29天。闰年每四年一次,在判断闰年时,可以使用年份除于4,如果能够整除,则该年是闰年。 本案例要求编写一个程序,…

项目前端遇到的相关问题及解决办法

一.在IDEA中修改html文件后,网页却不更新,即使重启服务,页面也不更新 由于本地静态网页缓存,导致浏览器不访问服务器的新资源,解决办法如下: 打开网页后,按“F12”调出“开发人员工具”。长按浏览器左上角的“刷新”按钮直至弹出对话框,选择“清空缓存并进行硬刷新”。…

python e怎么表示

exp()方法返回x的指数,ex。 语法 以下是 exp() 方法的语法: import math math.exp( x ) 注意:exp()是不能直接访问的,需要导入 math 模块,通过静态对象调用该方法。 参数 x -- 数值表达式。 返回值 返回x的指数,…

01背包问题求解

来源于 https://kamacoder.com/problempage.php?pid1046 使用动态规划,五步走 1.定义状态数组和具体状态含义: dp是个二维数组,第一维代表物品索引,第二维代表背包空间状态。 dp[i][j]是指物品i 在背包空间j 的情况下所能放的…

【redis】redis安装

1、安装前准备 1.1环境准备 VMware安装 参考博文:【VMware】VMware虚拟机安装_配置_使用教程_选择虚拟机配置选项,设置dvd镜像为 点击启动虚拟机-CSDN博客 安装centOS的linux操作系统 xshell xftp 参考博文:【Linux】Xshell和Xftp简介_安装_VMwar…

最新版Git安装指南使用指南

首先,访问Git的官方网站https://git-scm.com下载适用于您操作系统的安装包。您也可以选择使用阿里云镜像来加速下载过程。 也可以用国内地址下载https://pan.quark.cn/s/0293d76e58bchttps://pan.quark.cn/s/0293d76e58bc安装过程 在这里插入图片描述 2、点击“…

vue3 Cesium 离线地图

源码:cesium-demo: Cesium示例工程,基于vue3 1、vite-plugin-cesium 是一个专门为 Vite 构建工具定制的插件,用于在 Vite 项目中轻松使用 Cesium 库。它简化了在 Vite 项目中集成 Cesium 的过程。 npm i cesium vite-plugin-cesium vite -D…

前端 JS 经典:变量交换

将两个变量值相互交换的方法。 1. 定义第三个变量 let a 5; let b 6;const temp b; b a; a temp;console.log(a, b); // 6 5 2. 使用解构 let a 5; let b 6;[a, b] [b, a];console.log(a, b); // 6 5

【AI】DeepStream(12):三维动作识别 deepstream-3d-action-recognition-示例演示

【AI】AI学习目录汇总 1、简介 DeepStream还可以分析视频帧序列,来识别视频中的动作。 示例参见:/opt/nvidia/deepstream/deepstream/sources/apps/sample_apps/deepstream-3d-action-recognition 2、编译 1)进入源码目录:/opt/nvidia/deepstream/deepstream/sources/a…

redis 定时任务锁 分布式锁

基于 redisTemplate 在分布式集群环境中的最佳实践,其实无论是单机还是集群,保证原子性都是第一位的,如果能同时保证性能和高可用,那么就是一个可靠的分布式锁解决方案。 主要思路是:设置锁时,使用 redis…

[leetcode]k-th-smallest-in-lexicographical-order 字典序的第K小数字

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int getSteps(int curr, long n) {int steps 0;long first curr;long last curr;while (first < n) {steps min(last, n) - first 1;first first * 10;last last * 10 9;}return steps;}int find…

WEB界面上使用ChatGPT

&#xff08;作者&#xff1a;陈玓玏&#xff09; 开源项目&#xff0c;欢迎star哦&#xff0c;https://github.com/tencentmusic/cube-studio 随着大模型不断发展&#xff0c;现在无论写代码&#xff0c;做设计&#xff0c;甚至老师备课、评卷都可以通过AI大模型来实现了&…

开发小技巧Tips-----在Idea中配置nacos/redis等

背景&#xff1a; 进入了一个新的项目开发&#xff0c;领导为了加快开发速度&#xff08;加快调试的速度&#xff09;&#xff0c;让我们在本地启动服务&#xff0c;然后给了我一堆数据就走了。坏了坏了&#xff0c;啥意思啊&#xff0c;自己开发的时候本地就是直接点击一下run…

在vscode 中ssh连接虚拟ubuntu,不能使用code打开文件

这是参考别人的文章&#xff1a;https://blog.csdn.net/weixin_44465434/article/details/130035032找到vscode的版本信息&#xff0c;提交后面是需要的打开home/(用户)/.bashrc&#xff0c;添加环境变量 export PATH"~/.vscode-server/bin/5437499feb04f7a586f677b155b03…

江协科技51单片机学习- p16 矩阵键盘

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

LeetCode 算法:验证二叉搜索树 c++

原题链接&#x1f517;&#xff1a;验证二叉搜索树 难度&#xff1a;中等⭐️⭐️ 题目 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左 子树 只包含 小于 当前节点的数。节点的右子树只包含 大于…