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
:错误对象,如果没有错误则为nullfd
:文件的描述符
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的其他操作
- 验证路径是否存在:
- fs.exists(path , callback)
- fs.existsSync(path)
var fs = require("fs")
var isExists = fs.existsSync("a.txt")
console.log(isExists)
输出:true
- 获取文件信息:
- 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()是否是一个文件爽
- 删除文件
- fs.unlink(path, callback)
- fs.unlinkSync(path)
var fs = require("fs")
fs.unlinkSync("a.txt")
- 列出文件
- 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', '文件系统', '简介' ]
- 截断文件
- fs.truncate(path, len, callback)
- fs.truncateSync(path, len)
截断文件:将文件按设置成指定的len大小
var fs = require("fs")
fs.truncateSync("hello.txt",10)
输出:
- 建立目录
- fs.mkdir(path[, mode], callback)
- fs.mkdirSync(path[, mode])
var fs = require("fs")
fs.mkdirSync("a")
就创建了一个a文件夹
- 删除目录
- fs.rmdir(path, callback)
- fs.rmdirSync(path)
var fs = require("fs")
fs.rmdirSync("a")
- 重命名文件和目录
- 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("移动成功")
}
})
这样文件就移动到桌面上了。
- 监视文件更改写入
- 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就会检查一次