文件系统:文件系统简单来说就是通过Node来操作系统中的文件 。
在Node中,与文件系统的交互是非常重要的,服务器的本质就将本地的文件
发送给远程的客户端。
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是单线程的所以一旦同步阻塞整个网页就卡在那里了。
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]])
文件的描述符
作为结果,我们可以通过该描述符来对文件进行各种操作。var fs = require("fs")
var fd = fs.openSync('hello.txt', 'w')
console.log(fd)
输出:
3
3就是该文件的编号
fs.open(path[, flags[, mode]], callback)
err
:错误对象,如果没有错误则为nullfd
:文件的描述符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]])
var fs = require("fs")
var fd = fs.openSync('./hello.txt', 'w')
fs.writeSync(fd,"天气真不错")
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)
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])
文件的路径
及文件名
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)
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])
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("流关闭了")
})
流打开了
流关闭了
fs.readSync(fd, buffer[, options])
fs.read(fd[, options], callback)
fs.readFileSync(path[, options])
fs.readFile(path[, options], callback)
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)
})
var fs = require("fs")
var isExists = fs.existsSync("a.txt")
console.log(isExists)
输出:true
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的属性:
var fs = require("fs")
fs.unlinkSync("a.txt")
var fs = require("fs")
fs.readdir(".",function (err,files){
if (!err) {
// files是一个字符串数组,每一个元素就是一个文件夹或文件的名字
console.log(files)
}
})
输出:
[ 'hello.txt', 'node_modules', '文件系统', '简介' ]
var fs = require("fs")
fs.truncateSync("hello.txt",10)
var fs = require("fs")
fs.mkdirSync("a")
var fs = require("fs")
fs.rmdirSync("a")
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("移动成功")
}
})
在回调函数中会有两个参数:
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就会检查一次