2022-08-29 NodeJS文件系统

82 阅读7分钟

模块是什么

在Node中模块是module,每个文件都被视为独立的模块,后来提出的CommonJS规范,使JS可以运行在任何地方、任何平台

CommonJS对模块的定义十分简单:

  • 模块引用
  • 模块定义
  • 模块标识

模块化的好处:

  1. 为了降低程序之间的耦合性
  2. 提高程序代码的复用性

在node中,通过require()函数引入来自外部的模块,require的参数是一个文件的路径

模块化:

  • 在Node中,一个js文件就是一个模块
  • 在Node中,每一个js文件代码都是独立运行在一个函数中,而不是全局作用域,所以一个模块中的变量和函数在其他模块中无法访问
  • 可以通过exports来向外暴露变量和方法,只需设置为exports的属性即可

模块分为两大类:

  • 核心模块

      - 由node引擎提供的模块
      - 核心模块的标识,就是模块的名字
    
  • 文件模块

      - 由用户自己创建的模块
      - 文件模块的标识就是文件的路径(绝对路径、相对路径)
    

在node中有一个全局变量global,它的作用和网页中的window类似,在全局中创建的变量都会作为global的属性保存

当node在执行模块中的代码时,它会把内容包装在一个函数里,如下:

打印这段话时console.log(arguments.callee + '')会输出以下代码

function (exports, require, module, __filename, __dirname) {
    // 模块中的内容
}

参数说明:

  • exports 该对象用来将变量或函数暴露到外部,与module.exports指向同一个引用
  • require 是一个函数,用来引入外部的模块
  • module 代表是当前模块本身,exports就是module的属性,即可以用exports导出也可以用module.exports导出
  • __filename 当前模块的完成路径
  • __dirname 当前模块所在文件夹的完成路径

注意: exports和module.exports的区别:

  • 本质上指向的是同一个对象

  • 通过exports只能使用.的方式来向外暴露内部变量

      exports.xxx = xxx
    
  • 而module.exports即可以通过.的形式,也可以直接赋值

      module.exports.xxx = xxx
      module.exports = {}
    
  • 如果exports通过赋值,则改变了对象引用,和module.exports不在是同一个对象指向

Buffer缓冲区

  • Buffer的结构和数组很像,操作的方法也和数组类似

  • 数组中不能存储二进制文件,而Buffer就是专门用来存储二进制的数据

  • 使用Buffer不需要引入模块,直接使用即可

  • Buffer中的存储的都是二进制数据,但是在显示时都是16进制格式

  • Buffer中每个元素的范围都是从00-ff(16进制) 0-255(10进制) 00000000-11111111(2进制)

  • 计算机中的一个0或一个1称为1位(bit)

      - 8bit = 1byte(字节)
      - 1024byte = 1KB
      - 1024KB = 1MB
      - 1024MB = 1GB
      - 1024GB = 1TB
    
  • Buffer中一个元素,占用内存的一个字节

文件系统fs模块

文件系统

  • 就是通过node来操作系统的文件
  • 使用文件系统,需要现引入fs模块,fs是核心模块,直接引入不需要下载

同步文件的写入

手动操作的步骤:

  1. 打开文件

    fs.openSync(path, flagsp[, mode])

    • path 要打开文件的路径

    • flags 打开文件要做的操作类型

        r 只读
        w 可写
      
    • mode 设置文件的操作权限,一般不传

    返回值:

    • 该方法会返回一个文件的描述符作为结果,可以通过该描述符来对文件进行各种操作
  2. 向文件中写入内容

    fs.writeSync(fd, string[, position[, encoding]])

    • fd 文件的描述符,需要传递写入的文件的描述符
    • string 要写入的内容
  3. 保存并关闭文件 fs.closeSync(fd)

    • fd 文件的描述符

异步文件的写入

fs.open(path, flags[, mode], callback)

  • 用来打开一个文件

  • 异步调用的方法,结果都是通过回调函数返回的

  • 回调函数两个参数

    err 错误对象,如果没有错误则为null fd 文件的描述符

fs.write(fd, buffer[, offset[, length[, position]]], callback)

  • 用来写入文件

fs.close(fd)

  • 关闭文件
var fs = require("fs");

// 异步文件打开没有返回值-文件描述符
fs.open("hello2.txt", "w", function (err, fd) {
  // 判断打开是否出错
  if (!err) {
    // 写入文件
    console.log("打开文件成功");
    fs.write(fd, "写入异步文件的内容", function (err) {
      // 判断写入是否出错
      if (!err) {
        console.log("写入文件成功");
        // 关闭文件
        fs.close(fd, function (err) {
          if (!err) console.log("关闭文件成功");
        });
      }
    });
  } else {
    console.log(err);
  }
});

简单文件的写入

fs.writeFile(file, data[, options], callback)

fs.writeFileSync(file, data[, options])

  • file 要操作的文件的路径

  • data 要写入的数据

  • options 选项,可以对写入进行一些设置

    对象

{
    - flags
      r 只读
      w 可写
      a 追加
      ...
}
  • callback 当写入完成以后执行的函数
// 引入模块
var fs = require("fs");

// 绝对路径C:\\Users\\admin\\Desktop\\test.txt
fs.writeFile("hello3.txt", "这是通过writeFile写入的内容", { flag: "a" }, function (err) {
  if (!err) {
    console.log("写入成功");
  }
});

流式文件的写入

同步、异步、简单文件都不适合大文件的写入,性能较差,容易导致内存溢出

流式文件的写入

  1. 创建一个可写流
/**
 * fs.createWriteStream(path[, options])
 * - 用来创建一个可写流
 * - path-文件路径
 * - options 配置参数
 */
  1. 可以通过监听流的open和close事件来监听流的打开和关闭
/**
 * on(事件字符串, 回调函数)
 * - 可以为对象绑定一个事件
 * once(事件字符串,回调函数)
 * - 可以为对象绑定一个一次性的事件,该事件将会在触发一次以后自动失效
 */
var fs = require("fs");
var ws = fs.createWriteStream("hello4.txt");

// 通过ws向文件中输出内容
ws.write("通过可写流写入文件的内容");
ws.write("锄禾日当午");
ws.write("汗滴禾下土");

// ws.close();
ws.end();

// 监听事件
ws.once("open", function () {
  console.log("流打开了~");
});

ws.once("close", function () {
  console.log("流关闭了~");
});

文件的读取

  1. 同步文件的读取

  2. 异步文件的读取

  3. 简单文件的读取

    fs.readFile(path[, options], callback)

    fs.readFileSync(path[, options])

    • path 文件路径

    • options 读取的选项

    • callback 回调函数,通过回调函数将数据读取到内容,返回(err, data)

        err 错误对象
        data 读取到的数据,返回一个Buffer
      
  4. 流式文件的读取:流式文件读取也适用大文件,可以多次将文件读取到内存中

var fs = require("fs");

fs.readFile("测试1.jpg", function (err, data) {
  if (!err) {
    // console.log(data.toString()); // 如果是字符串
    // console.log(data); // 如果是图片
    // 写入到其他文件中
    fs.writeFile("hello.png", data, function (err) {
      console.log("图片复制成功");
    });
  }
});

var fs = require("fs");
// 创建一个可读流
var rs = fs.createReadStream("./hello2.txt");
// 创建一个可写流
var ws = fs.createWriteStream("copy1.txt");

// 监听可读流的开启和关闭
rs.once("open", function () {
  console.log("可读流打开了~");
});

rs.once("close", function () {
  console.log("可读流关闭了~");
  //   ws.close();
});

// 监听可写流的开启和关闭
ws.once("open", function () {
  console.log("可写流打开了~");
});

ws.once("close", function () {
  console.log("可写流关闭了~");
});

// 如果要读取一个可读流中的数据,必须要为可读流绑定一个data事件,data事件绑定完毕,它会自动开始读取数据
/* rs.on("data", function (data) {
  //   console.log(data.length);
  // 将读取到的数据写入到可写流中
  ws.write(data);
}); */

// pipe()可以将可读流中的内容直接输出到可写流中
rs.pipe(ws);

fs中其它方法

1、fs.existsSync(path) 检查文件是否存在

/**
 * fs.existsSync(path) 检查文件是否存在
 */
 
var fs = require("fs");
var isExist = fs.existsSync("111.js");
console.log(isExist);

2、fs.stat(path, callback)与fs.statSync(path)

  • 获取文件的状态
  • 返回一个对象,对象中保存了当前状态的相关信息
fs.stat("./01_buffer.js", function (err, stat) {
  if (!err) {
    console.log(stat.isFile()); // 是否是一个文件
    console.log(stat.isDirectory()); // 是否是一个文件夹(目录)
  }
});

3、fs.unlink(path, callback)与fs.unlinkSync(path)

  • 删除文件

fs.unlinkSync("hello.txt");

4、fs.readdir(path[, options], callback)与fs.readdirSync(path[, options])

  • 读取一个目录的结构

    回调函数中的files是一个字符串数组,每个元素就是一个文件夹或文件的名字

fs.readdir(".", function (err, files) {
  if (!err) {
    console.log(files);
  }
});

5、 fs.ftruncate(fd[, len], callback)与fs.ftruncateSync(fd[, len])

  • 截断文件,将文件修改为指定的大小,中文大小为3个字节,多余的会乱码 fs.truncateSync("hello2.txt", 10)

6、fs.mkdir(path[, mode], callback)与fs.mkdirSync(path[, mode])

  • 创建一个目录

7、 fs.rmdir(path, callback)与fs.rmdirSync(path)

  • 删除一个目录
fs.mkdirSync("hello");
fs.rmdirSync("hello");

8、fs.rename(oldPath, newPath, callback)与fs.renameSync(oldPath, newPath)

  • 对文件进行重命名

  • 参数

    oldPath 旧的路径 newPath 新的路径 callback 回调函数 fs.renameSync("copy.txt", "命名.txt");

9、fs.watchFile(filename[, options], listener)

  • 监听文件的修改

  • 参数:

      filename 要监听的文件路径
      options 配置选项
      listerner 回调函数,当文件发生变化时,回调函数会执行,在回调函数中会有两个参数:
              current 当前文件的状态
              previous 修改前文件的状态
              这两个对象都是Stats对象
    
var fs = require('fs');

fs.watchFile("hello2.txt", { interval: 1000 }, function (current, previous) {
  console.log("修改前文件大小:" + previous.size);
  console.log("修改后文件大小:" + current.size);
});