node内置模块——fs文件系统模块

文件系统的定义

文件系统:文件系统简单来说就是通过Node来操作系统中的文件 。

文件系统的重要作用

在Node中,与文件系统的交互是非常重要的,服务器的本质就将本地的文件发送给远程的客户端。

fs模块

Node通过fs模块来和文件系统进行交互。
fs模块提供了一些标准文件访问API来打开、读取、写入文件,以及与其交互。
fs是核心模块,直接引入不需要下载。

var fs = require("fs")

同步和异步调用

fs模块中所有的操作都有两种形式可供选择同步和异步:

  • 同步文件系统会阻塞程序的执行,也就是除非操作完毕,否则不会向下执行代码。
  • 异步文件系统不会阻塞程序的执行,而是在操作完成时,通过回调函数将结果返回。异步的方法都有回调函数

我们更常用的是异步方法。

目录的创建

创建目录

// 创建目录
fs.mkdir("./avatar", (err) => {
    console.log(err)
    if (err && err.code === 'EEXIST') {
        console.log('目标已存在')
    }
})

重命名目录

fs.rename("./avatar", "./avatar2", (err) => {
    console.log(err)
    if (err && err.code === 'ENOENT') {
        console.log('目录不存在')
    }
})

目录的删除

// 删除
fs.rmdir("./avatar2", err => {
    console.log(err)
    if (err && err.code === 'ENOENT') {
        console.log('目录不存在')
    }
})

如果目录不为空不会直接删除,要先删除文件才能删除目录。

目录的读取

fs.readdir("./avatar", (err, data) => {
    if (!err) {
        console.log(data)
    }
    console.log(err)
})
  • 查看目录下文件的具体信息
fs.stat("./avatar", (err, data) => {
    console.log(data)
})
fs.stat("./avatar", (err, data) => {
    //是否是目录
    console.log(data.isDirectory())
    //是否是文件
    console.log(data.isFile())
})

包含文件的目录的删除

  • 同步删除
fs.readdir("./avatar", (err, data) => {
    data.forEach(item => {
        // 使用同步方法进行删除,以为嫩文件删除完才能删除目录
        fs.unlinkSync(`./avatar/${item}`)
    })

    fs.rmdir("./avatar",(err)=>{console.log(err)})
})

同步问题:由于JS是单线程的所以一旦同步阻塞整个网页就卡在那里了。

  • 异步删除——基于Promise
const fs = require("fs").promises
fs.readdir("./avatar2").then(async (data) => {
    let arr=[]
    data.forEach(item => {
       arr.at.push(fs.unlink(`./avatar2/${item}`))
    })
    // Promise.all([]),等待数组中的内容都执行完才往下执行
    await Promise.all(arr)
    await fs.rmdir("./avatar2")
})

或者

const fs = require("fs").promises
fs.readdir("./avatar2").then(async (data) => {
    await Promise.all(data.map(item=>fs.unlink(`./avatar/${item}`)))
    await fs.rmdir("./avatar2")
})

所以关于文件的操作都有基于require("fs").promises的另一种写法:

//读取文件
fs.readFile("./文件读取.js", "utf-8").then(data => {
    console.log(data)
})

文件的写入

文件的同步和异步写入

打开文件
  • 同步:fs.openSync(path[, flags[, mode]])
    path:要打开文件的路径
    flags:打开文件要做的操作类型:r:只读,w:可写的
    mode: 设置文件的操作权限,一般不传
    该方法会返回一个文件的描述符作为结果,我们可以通过该描述符来对文件进行各种操作。
var fs = require("fs")

var fd = fs.openSync('hello.txt', 'w')
console.log(fd)

输出:

3

3就是该文件的编号

  • 异步:fs.open(path[, flags[, mode]], callback)
    callback是必须的。
    异步调用的结果都是通过回调函数返回的,而不通过return返回。
    我们一般取回调函数的参数arguments:
    • err:错误对象,如果没有错误则为null
    • fd:文件的描述符
      eg:
var fs = require("fs")

var fd = fs.open('hello.txt', 'w',function (err,fd){
    if (!err) {
       console.log(fd)
    } else {
        console.log(err)
   }
})

输出:

3

异步的体现:

var fs = require("fs")

var fd = fs.open('hello.txt', 'w',function (err,fd){
    if (!err) {
        console.log("open done")
    } else {
        console.log(err)
   }
})

console.log("after open")

输出:

after open
open done

我们发现先输出的after open,再输出open done。这就是因为服务器在下完open的命令之后将open的过程交给其他线程,而自己继续向下执行,当open完成之后再执行回调函数,所以open done 在after open之后。
在这里插入图片描述

向文件写内容
  • 同步:fs.writeSync(fd, string[, position[, encoding]])
    fd:要写入的文件位置+文件名
    如果文件不存在会自动创建
    string:要写入的内容
    写入的内容默认会覆盖问文件原本的内容
    position:写入的起始位置
    encoding:写入的编码,默认utf-8
var fs = require("fs")

var fd = fs.openSync('./hello.txt', 'w')
fs.writeSync(fd,"天气真不错")

输出:
hello.txt
在这里插入图片描述

  • 异步:fs.write(fd, string[, position[, encoding]], callback)
    用来异步写入文件。
var fs = require("fs")

var fd = fs.open('hello.txt', 'w',function (err,fd){
    if (!err) {
        // 进行写入
        fs.write(fd,"这是异步写入的内容",function (err){
            if (!err) {
                console.log("写入成功")
            }      
        })
    } else {
        console.log(err)
   }
})
保存并关闭

如果文件不关闭会占用大量的内存.

  • 同步:fs.closeSync(fd)
    fd:要关闭的文件的描述符
var fs = require("fs")

var fd = fs.openSync('./hello.txt', 'w')
fs.writeSync(fd, "天气真不错")
fs.closeSync(fd)
  • 异步:fs.close(fd[, callback])
var fs = require("fs")

var fd = fs.open('hello.txt', 'w',function (err,fd){
    if (!err) {
        // 进行写入
        fs.write(fd,"这是异步写入的内容",function (err){
            if (!err) {
                console.log("写入成功")
            }
            // 关闭
            fs.close(fd,function (err){
                if (!err) {
                   console.log("文件关闭成功")
               }
            })
        })
    } else {
        console.log(err)
   }
})

输出:

写入成功
文件关闭成功

同步是人类的思维习惯,异步可以提升计算机的运行速度。

简单文件写入

  • 同步:fs.writeFileSync(file, data[, options])
    file:要操作文件的路径文件名
    data:要写入的数据
    options:选项,可以对写入进行一些设置,一般省略

options一般是一个对象,有如下属性:
(1)encoding:默认=‘utf8’
(2)mode:默认=00666
(3)flag:默认=‘w’ ,控制文件的打开状态
r:读 ; w:写 ;a:追加

eg:

var fs = require("fs")

fs.writeFile("C:\\Users\\86198\\Desktophello.txt", "简单写入",{flag:"w"})
  • 异步:fs.writeFile(file, data[, options], callback)
    callback:写入完成以后执行的函数
var fs = require("fs")

fs.writeFile("hello.txt", "简单写入", function (err) {
    if (!err) {
        console.log("写入成功")
    }
})

在这里插入图片描述
不用再打开和关闭文件了。writeFile是对打开、写入和保存文件的封装。

文件追加写入

默认情况下像文件写入会直接覆盖原来的内容,可以使用追加方法appendFile,向文件中追加内容。

fs.appendFile("./avatar/a.txt", "\nhello",(err)=> {
    console.log(err)
})

文件写入的特点

w 模式的特点:打开文件用于写操作,如果不存在则创建,如果存在则覆盖文件原本内容

文件的打开状态

标识符状态
r读取文件,文件不存在则出现异常
r+读写文件,文件不存在则出现异常
rs在同步模式下打开文件用于读取
rs+在同步模式下打开文件用于读写
w打开文件用于写操作,如果不存在则创建,如果存在则截断
wx打开文件用于写操作,如果存在则打开失败
w+打开文件用于读写,如果不存在则创建,如果存在则截断
wx+打开文件用于读写,如果存在则打开失败
a打开文件用于追加,如果不存在则创建
ax打开文件用于追加,如果路径存在则失败
a+打开文件进行读取和追加,如果不存在则创建该文件
ax+打开文件进行读取和追加,如果路径存在则失败
可以通过属性控制文件的打开状态

流式文件写入

同步、异步、简单文件的写入都是一次性的写入,即以一次性就将所有内容从一个文件写入到另一个文件。缺点是都不适合大文件的写入,性能较差,容易导致内存溢出。

流式文件写入就是一次向文件中写入一点,不会造成内存溢出。

创建可写流:fs.createWriteStream(path[, options])

  • path:文件路径
  • options:配置参数
var fs = require("fs")
var ws = fs.createWriteStream("hello.txt")
// 写内容
ws.write("可写流写入\n")
ws.write("不会被覆盖")
// 关闭流
ws.end()
// 不能用ws.close()


// 监听流的open和close时间来监听流的打开和关闭
// on是绑定长期有限的事件,once是绑定一次性事件
ws.once("open",function (){
   console.log("流打开了")
})
ws.on("close",function (){
   console.log("流关闭了")
})

hello.txt:
在这里插入图片描述
控制台输出:

流打开了
流关闭了

文件的读取

同步文件读取

fs.readSync(fd, buffer[, options])

异步文件读取

fs.read(fd[, options], callback)

简单文件读取

  • 同步:fs.readFileSync(path[, options])
  • 异步:fs.readFile(path[, options], callback)
    path要读取的文件的路径
    options读取的选项
    callback回调函数,通过回调函数将读取到内容返回
    回调函数有两个参数:err,data
    data是读取到的数据是一个buffer

eg:

var fs = require("fs")
fs.readFile("hello.txt",function (err,data){
    if (!err) {
       console.log(data.toString())
   }
})

输出:

hello
我是hello.txt

流式文件读取

流式文件读取也适用于一些比较大的文件,可以分多次将文件读取到内存中
fs.createReadStream(path[, options])

var fs = require("fs")
var rs = fs.createReadStream("hello.txt")

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

rs.once("open",function (){
   console.log("流打开了")
})
rs.on("close",function (){
   console.log("流关闭了")
})

输出:

流打开了
<Buffer 68 65 6c 6c 6f 0a e6 88 91 e6 98 af 68 65 6c 6c 6f 2e 74 78 74>
流关闭了

这里文件比较小所以只读取了一次。

流式文件实现文件的读取和写入

传统写法

var fs = require("fs")
var rs = fs.createReadStream("hello.txt")
var ws = fs.createWriteStream("a.txt")

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


rs.once("open",function (){
   console.log("可读流打开了")
})
rs.on("close",function (){
    console.log("可读流关闭了")
    // 挂壁
    ws.end()
})
ws.once("open",function (){
   console.log("可写流打开了")
})
ws.on("close",function (){
   console.log("可写流关闭了")
})


输出:

可读流打开了
可写流打开了
可读流关闭了
可写流关闭了

在这里插入图片描述

简单写法

可以在可读流和可写流之间建设管道,可以直接将可读流中的内容输出到可写流。
pipe ()可以将可读流中的内容,直接输出到可写流中

var fs = require("fs")
var rs = fs.createReadStream("hello.txt")
var ws = fs.createWriteStream("a.txt")

rs.pipe(ws)

在这里插入图片描述

文件的删除

fs.unlink("./avatar/a.txt",err=> {
    console.log(err)
})

fs的其他操作

  1. 验证路径是否存在:
  • fs.exists(path , callback)
  • fs.existsSync(path)
var fs = require("fs")

var isExists = fs.existsSync("a.txt")
console.log(isExists)

输出:true

  1. 获取文件信息:
  • fs.stat(path, callback)
  • fs.statSync(path)
    获取文件的状态
    它会给我们返回一个对象,这个对象中保存了当前对象状态的相关信息。
var fs = require("fs")

fs.stat("a.txt",function (err,stat){
   console.log(stat)
})

输出:

Stats {
  dev: 1190885400,
  mode: 33206,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  blksize: 4096,
  ino: 13229323907181608,
  size: 21,
  blocks: 0,
  atimeMs: 1653674170186.6125,
  mtimeMs: 1653674152915.8928,
  ctimeMs: 1653674152915.8928,
  birthtimeMs: 1653674152899.5056,
  atime: 2022-05-27T17:56:10.187Z,
  mtime: 2022-05-27T17:55:52.916Z,
  ctime: 2022-05-27T17:55:52.916Z,
  birthtime: 2022-05-27T17:55:52.900Z
}

stat的属性:

  • size文件的大小
  • isFile()是否是一个文件
  • isDirectory()是否是一个文件爽
  1. 删除文件
  • fs.unlink(path, callback)
  • fs.unlinkSync(path)
var fs = require("fs")
fs.unlinkSync("a.txt")
  1. 列出文件
  • fs.readdir(path[, options], callback)
  • fs.readdirSync(pathl, options])
    回调函数中有参数err和files
    files是一个字符串数组,每一个元素就是一个文件夹或文件的名字
var fs = require("fs")

fs.readdir(".",function (err,files){
    if (!err) {
    //    files是一个字符串数组,每一个元素就是一个文件夹或文件的名字
        console.log(files)
   }
})

输出:

[ 'hello.txt', 'node_modules', '文件系统', '简介' ]
  1. 截断文件
  • fs.truncate(path, len, callback)
  • fs.truncateSync(path, len)
    截断文件:将文件按设置成指定的len大小
var fs = require("fs")

fs.truncateSync("hello.txt",10)

输出:
在这里插入图片描述

  1. 建立目录
  • fs.mkdir(path[, mode], callback)
  • fs.mkdirSync(path[, mode])
var fs = require("fs")

fs.mkdirSync("a")

就创建了一个a文件夹
在这里插入图片描述

  1. 删除目录
  • fs.rmdir(path, callback)
  • fs.rmdirSync(path)
var fs = require("fs")

fs.rmdirSync("a")
  1. 重命名文件和目录
  • fs.rename(oldPath, newPath, callback)
  • fs.renameSync(oldPath, newPath)

oldPath 旧的路径
newPath新的路径
callback 回调函数

var fs = require("fs")

fs.rename("hello.txt","a.txt",function (err){
    if (!err) {
       console.log("重命名成功")
   }
})

也可以实现文件移动的功能:

var fs = require("fs")

fs.rename("a.txt","C:\\Users\\86198\\Desktop\\a.txt",function (err){
    if (!err) {
       console.log("移动成功")
   }
})

这样文件就移动到桌面上了。
在这里插入图片描述

  1. 监视文件更改写入
  • fs.watchFile(filename[, options], listener)
    监视文件的修改
    filename要监视的文件的名字
    options配置选项
    listener 回调函数,当文件发生变化时,回调函数会执行

在回调函数中会有两个参数:
curr当前文件的状态
prev修改前文件的状态
——这两个对象都是stats对象
可以通过这两个对象获取到文件的具体变化

var fs = require("fs")

fs.watchFile("hello.txt",function (prev,curr){
    console.log("文件发生变化了")
    console.log("修改前文件的大小:" + prev.size)
    console.log("修改后文件的大小:"+curr.size)
})

输出:
在这里插入图片描述

是通过定时机制进行文件的检查,所以有时可能需要等一会才能出现结果。
可以通过修改options的interval属性来设置时间

var fs = require("fs")

fs.watchFile("hello.txt", {interval:1000},function (prev,curr){
    console.log("文件发生变化了")
    console.log("修改前文件的大小:" + prev.size)
    console.log("修改后文件的大小:"+curr.size)
})

这样隔1s就会检查一次