三、Node常用的内置模块

116 阅读6分钟

内置模块path

  • 从路径中获取信息

    • dirname:获取文件的父文件夹;

    • basename:获取文件名;

    • extname:获取文件扩展名;

  • 路径的拼接

    • 如果我们希望将多个路径进行拼接,但是不同的操作系统可能使用的是不同的分隔符;

    • 这个时候我们可以使用path.join函数;

  • 将文件和某个文件夹拼接

    • 如果我们希望将某个文件和文件夹拼接,可以使用 path.resolve;

    • resolve函数会判断我们拼接的路径前面是否有 /或../或./;

    • 如果有表示是一个绝对路径,会返回对应的拼接路径;

    • 如果没有,那么会和当前执行文件所在的文件夹进行路径的拼接

内置模块fs

  • fs是File System的缩写,表示文件系统。

    • 对于任何一个为服务器端服务的语言或者框架通常都会有自己的文件系统:

    • 因为服务器需要将各种数据、文件等放置到不同的地方;

    • 比如用户数据可能大多数是放到数据库中的(后面我们也会学习);

    • 比如某些配置文件或者用户资源(图片、音视频)都是以文件的形式存在于操作系统上的;

  • Node也有自己的文件系统操作模块,就是fs:

    • 借助于Node帮我们封装的文件系统,我们可以在任何的操作系统(window、Mac OS、Linux)上面直接去 操作文件;

    • 这也是Node可以开发服务器的一大原因,也是它可以成为前端自动化脚本等热门工具的原因;

fs的API介绍

  • Node文件系统的API非常的多:

    • 我们不可能,也没必要一个个去学习;

    • 这个更多的应该是作为一个API查询的手册,等用到的时候查询即可;

  • 但是这些API大多数都提供三种操作方式:

    • 方式一:同步操作文件:代码会被阻塞,不会继续执行;

    • 方式二:异步回调函数操作文件:代码不会被阻塞,需要传入回调函数,当获取到结果时,回调函数被执行;

    • 方式三:异步Promise操作文件:代码不会被阻塞,通过 fs.promises 调用方法操作,会返回一个Promise, 可以通过then、catch进行处理;

fs的API

  • 这些API大多数都提供三种操作方式:

    • 方式一 :同步操作文件:代码会被阻塞,不会继续执行;

    • 方式二:异步回调函数操作文件:代码不会被阻塞,需要传入回调函数,当获取到结果时,回调函数被执行;

    • 方式三:异步Promise操作文件:代码不会被阻塞,通过 fs.promises 调用方法操作,会返回一个Promise, 可以通过then、catch进行处理;

const fs = require("fs");
//读取文件信息
const filePath = "./abc.txt";

//1同步操作
const info1 = fs.statSync(filePath);
console.log("后续需要执行的代码");
console.log(info1);

//2异步操作
fs.stat(filePath, (err, info) => {
  if (err) {
    console.log(err);
    return;
  }
  console.log(info);
});
console.log("后续需要执行的代码");

//3promise
fs.promises
  .stat(filePath)
  .then((info) => {
    console.log(info);
  })
  .catch((err) => {
    console.log(err);
  });
console.log("后续需要执行的代码");

文件描述符

  • 文件描述符(File descriptors)是什么呢?

    • 在 POSIX 系统上,对于每个进程,内核都维护着一张当前打开着的文件和资源的表格。

    • 每个打开的文件都分配了一个称为文件描述符的简单的数字标识符。

    • 在系统层,所有文件系统操作都使用这些文件描述符来标识和跟踪每个特定的文件。

    • Windows 系统使用了一个虽然不同但概念上类似的机制来跟踪资源。

  • 为了简化用户的工作,Node.js 抽象出操作系统之间的特定差异,并为所有打开的文件分配一个数字型的文件描述符。

  • fs.open() 方法用于分配新的文件描述符。

    • 一旦被分配,则文件描述符可用于从文件读取数据、向文件写入数据、或请求关于文件的信息。

文件的读写

  • 如果我们希望对文件的内容进行操作,这个时候可以使用文件的读写:

    • fs.readFile(path[, options], callback):读取文件的内容;

    • fs.writeFile(file, data[, options], callback):在文件中写入内容;

  • 在上面的代码中,你会发现有一个大括号没有填写任何的内容,这个是写入时填写的option参数:

    • flag:写入的方式。

      • w 打开文件写入,默认值;

      • w+打开文件进行读写,如果不存在则创建文件;

      • r+ 打开文件进行读写,如果不存在那么抛出异常;

      • r打开文件读取,读取时的默认值;

      • a打开要写入的文件,将流放在文件末尾。如果不存在则创建文件;

      • a+打开文件以进行读写,将流放在文件末尾。如果不存在则创建文件

    • encoding:字符的编码;

    const fs = require("fs");
      const content = " \n你好啊,Jay";
      //1.文件写入
      fs.writeFile("./abc.txt", content, { flag: "a" }, (err) => {
        if (err) {
          console.log(err);
        }
      });
      //2.文件读取
      fs.readFile("./abc.txt", { encoding: "utf8" }, (err, data) => {
        console.log(data);
      });
    

文件夹的操作

const fs = require("fs");
const path = require("node:path/win32");

const dirname = "./why";

//1创建文件夹
if (!fs.existsSync(dirname)) {
 fs.mkdir(dirname, (err) => {
   err ? console.log(err) : true;
 });
}

//2读取文件夹下的文件
function getFiles(dirname) {
 fs.readdir(dirname, { withFileTypes: true }, (err, files) => {
   files.forEach((file, index) => {
     if (file.isDirectory()) {
       const filePath = path.resolve(dirname, file.name);
       getFiles(filePath);
     } else {
       console.log(file.name);
     }
   });
 });
}

getFiles(dirname);

//3重命名
let timer = setTimeout(() => {
 fs.rename("./why", "./newMJL", (err) => {
   if (err) throw err;
   console.log("Rename complete!");
 });
 clearTimeout(timer);
}, 1000);

events模块

  • Node中的核心API都是基于异步事件驱动的:

    • 在这个体系中,某些对象(发射器(Emitters))发出某一个 事件;

    • 我们可以监听这个事件(监听器 Listeners),并且传入的回 调函数,这个回调函数会在监听到事件时调用;

  • 发出事件和监听事件都是通过EventEmitter类来完成的,它们都属 于events对象。

    • emitter.on(eventName, listener):监听事件,也可以使用 addListener;

    • emitter.off(eventName, listener):移除事件监听,也可以使 用removeListener;

    • emitter.emit(eventName[, ...args]):发出事件,可以携带一 些参数;

const EventEmitter = require("events");

//1创建发射器
const emitter = new EventEmitter();

//2监听某一个事件
//addListener是on的alias的简写
emitter.on("click", (args) => {
  console.log("1监听到了click事件", args);
});
const listener2 = (args) => {
  console.log("2监听到了click事件", args);
};
emitter.on("click", listener2);

//3发出一个事件
setTimeout(() => {
  emitter.emit("click", "myKnock");
  emitter.off("click", listener2);
  emitter.emit("click", "myKnock");
}, 2000);

常见的属性

  • EventEmitter的实例有一些属性,可以记录一些信息:

    • emitter.eventNames():返回当前 EventEmitter对象注册的事件字符串数组;

    • emitter.getMaxListeners():返回当前 EventEmitter对象的最大监听器数量,可以通过setMaxListeners() 来修改,默认是10;

    • emitter.listenerCount(事件名称):返回当前 EventEmitter对象某一个事件名称,监听器的个数;

    • emitter.listeners(事件名称):返回当前 EventEmitter对象某个事件监听器上所有的监听器数组;

方法的补充

  • emitter.once(eventName, listener):事件监听一次

  • emitter.prependListener():将监听事件添加到最前面

  • emitter.prependOnceListener():将监听事件添加到最前面,但是只监听一次

  • emitter.removeAllListeners([eventName]):移除所有的监听器