fs 文件系统
-
fs (file system) 文件系统,属于 node.js 的核心模块,用于操作文件(夹)
-
使用 fs,需要先引入该模块
-
核心模块的标识就是模块的名字,这里就是
fs
const fs = require('fs');
同步和异步调用
- fs 模块中的操作有 2 种形式:同步、异步
- 同步调用有可能阻塞程序的执行。同步执行会返回结果。
- 异步调用不会阻塞程序的执行。异步执行是通过回调函数获取结果。
路径问题
- 使用相对路径时,是相对项目根目录而言的
就是说,在项目根目录不同的情况下,文件执行的结果会不一样 - 开发中,尽量使用
__dirname
拼接路径
操作文件
writeFile[Sync]
fs.writeFile(path, data[, options], callBack)
:[创建文件并]写入数据
fs.writeFileSync(path, data[, options])
:返回undefined
path
:文件的路径data
:要写入的数据option
:配置对象encoding
:编码格式,默认为utf8
flag
:文件系统标志,默认为'w'
-设置数据;改为'a'
-追加数据
callBack
:回调函数,写入完成后调用;接收 1 个参数err
:出错时表示出错的信息,默认为null
const fs = require('fs');
let data = '新写入的数据';// 同步写入数据
fs.writeFileSync('./writeFileSync.txt', data);// 异步写入数据
fs.writeFile('./writeFile.txt', data,err => console.log("err", err) // err null
);
- 如果写入的文件不存在,会创建该文件并写入数据;
- 如果写入的文件中原来就有内容,原来的内容会被覆盖
如果想追加数据,可以设置flag: 'a'
const fs = require('fs');
let data = '追加的内容';// 同步追加数据
fs.writeFileSync('./writeFileSync.txt', data, { flag: 'a' }); // 设置 flag 为 a// 异步追加数据
fs.writeFile('./writeFile.txt', data, { flag: 'a' }, // 设置 flag 为 aerr => console.log("err", err)
);
appendFile[Sync]
fs.appendFile(path, data[, options], callback)
:[创建文件并]追加数据
fs.appendFileSync(path, data[, options]
:返回undefined
path
:文件的路径data
:写入的数据options
:配置对象encoding
:默认为'utf8'
,可以设为"buffer"
flag
:默认值为'a'
callback
:回调函数,追加完成后调用;接收 1 个错误参数err
const fs = require('fs');
let data = '追加的内容';// 同步追加数据
fs.appendFileSync('./appendFileSync.txt', data);// 异步追加数据
fs.appendFile('./appendFile.txt', data,err => console.log("err", err) // err null
);
readFile[Sync]
fs.readFile(path[, options], callback)
:读取文件数据
fs.readFileSync(path[, options])
:返回文件数据
path
:文件的路径options
:配置对象encoding
:数据的编码格式,默认为null
如果只设置encoding
可以直接写成 String 格式flag
:文件系统标志,默认为'r'
callback
:回调函数,接收 2 个参数:err
:出错信息,默认为null
data
:读取的数据
const fs = require('fs');// 同步读取数据
let data = fs.readFileSync('./writeFileSync.txt');
console.log("syncData", data); // <Buffer e6 96 b0...>
console.log("syncData + ''", data + ''); // 新写入的数据// 异步读取数据
fs.readFile('./writeFile.txt', (err, data) => {if (err) return console.log("err", err);console.log("data", data); // <Buffer e6 96 b0...>console.log("data + ''", data + ''); // 新写入的数据
});
- 读取出来的数据默认是 Buffer 格式的,可以将其转成字符串形式
- 读取成功 -
err
为null
、data
为文件内容;读取失败 -err
为错误信息,data
为undefined
unlink[Sync]
fs.unlink(path, callback)
:删除文件
fs.unlinkSync(path)
:返回undefined
path
:文件的路径callBack
:接收 1 个参数err
,默认为null
const fs = require('fs');// 同步删除文件
fs.unlinkSync('./writeFileSync.txt');// 异步删除文件
fs.unlink('./writeFile.txt',err => console.log("err", err) // err null
);
操作目录
mkdir[Sync]
fs.mkdir(path[, options], callback)
:创建目录
fs.mkdirSync(path[, options])
:返回undefined
-
path
:新建的文件夹的路径 -
options
:配置对象recursive
:是否可以递归创建,默认为false
-
callback
:回调函数,接收 1 个参数err
,默认为null
const fs = require('fs');// 同步创建文件夹
fs.mkdirSync('./mkdirSync');// 异步创建文件夹
fs.mkdir('./mkdir',err => console.log("err", err) // err null
);
通过配置 option
可以实现递归创建文件夹
const fs = require('fs');// 同步创建递归文件夹
fs.mkdirSync('./reMkdirSync/mkdirSync', { recursive: true });// 异步创建递归文件夹
fs.mkdir('./reMkdir/mkdir', { recursive: true },err => console.log("err", err) // err null
);
readdir[Sync]
fs.readdir(path[, options], callback)
:读取目录的内容
fs.readdirSync(path[, options])
:返回目录的内容
path
:文件夹的路径options
:配置对象encoding
:默认为'utf8'
、可设置为'buffer'
withFileTypes
:是否显现文件类型,默认为false
callback
:回调函数,接收 2 个参数err
、files
- 目录的内容
const fs = require('fs');// 同步获取目录信息
let syncFiles = fs.readdirSync('./reMkdirSync');
console.log("syncFiles", syncFiles); // syncFiles [ 'mkdirSync' ]// 异步获取目录信息
fs.readdir('./reMkdir',(err, files) => console.log("files", files) // files [ 'mkdir' ]
);
files
为数组,数组元素为该目录下的文件(夹)名
可以通过配置 option
显示文件的类型
const fs = require('fs');// 同步获取目录信息
let syncFiles = fs.readdirSync('./reMkdirSync', { withFileTypes: true });
console.log("syncFiles", syncFiles); // syncFiles [ Dirent { name: 'mkdirSync', [Symbol(type)]: 2 } ]// 异步获取目录信息
fs.readdir('./reMkdir', { withFileTypes: true },(err, files) => console.log("files", files) // files [ Dirent { name: 'mkdir', [Symbol(type)]: 2 } ]
);
files
为数组,数组元素为文件对象,对象属性为 name & type
rmdir[Sync]
fs.rmdir(path, callback)
:删除目录
fs.rmdirSync(path)
:返回undefined
path
:文件夹的路径callback
:回调函数,接收 1 个错误信息的参数err
,默认为null
const fs = require('fs');// 同步删除目录
fs.rmdirSync('./mkdirSync');// 异步删除目录
fs.rmdir("./mkdir", err => console.log("err", err)); // err null
rm[Sync]
fs.rm(path[, options], callback)
:删除文件(夹)
fs.rmSync(path[, options])
:返回undefined
-
path
:文件夹的路径 -
options
:配置对象-
force
:为true
时,如果path
不存在,则异常将被忽略,默认为false
-
recursive
:如果为true
,则执行递归删除,默认为false
。在递归模式下,操作将在失败时重试maxRetries
:表示重试次数,默认为0
(如果recursive
选项为false
,则忽略此选项)retryDelay
:重试之间等待的时间(以毫秒为单位),默认为100
(如果recursive
选项为false
,则忽略此选项)
-
-
callback
:回调函数,接收 1 个参数err
,默认为null
const fs = require("fs");// 同步、递归删除文件夹
fs.rmSync("./reMkdirSync", { recursive: true });// 异步、递归删除文件夹
fs.rm("./reMkdir", { recursive: true },err => console.log("err", err)
);
其他操作
rename[Sync]
fs.rename(oldPath, newPath, callback)
:文件(夹)重命名
fs.renameSync(oldPath, newPath)
:返回undefined
oldPath
:文件的原路径newPath
:文件的新路径callback
:回调函数,接收一个参数err
,默认为null
const fs = require('fs');// 同步重命名
fs.renameSync('./oldSync.txt', './newSync.txt');// 异步重命名
fs.rename('./old.txt', './new.txt',err => console.log("err", err) // err null
);
因为参数写的是路径,所以也可以在重命名的同时,把文件剪切到其他位置
const fs = require('fs');// 同步重命名 并剪切到 folder 文件夹下
fs.renameSync('./newSync.txt', './folder/newSync.txt');// 异步重命名 并剪切到 folder 文件夹下
fs.rename('./new.txt', './folder/new.txt',err => console.log("err", err) // err null
);
如果 newPath
已经存在,则它将被覆盖。 如果在 newPath
中有目录,则会引发错误
const fs = require('fs');// 同步剪切 并覆盖同名文件
fs.renameSync('./folder/newSync.txt', './newSync.txt');// 异步剪切 并覆盖同名文件
fs.rename('./folder/new.txt', './new.txt',err => console.log("err", err)
);
existsSync
fs.existsSync(path)
:查看文件(夹)是否存在
path
:文件的路径
- 如果文件存在,则返回
true
;否则返回false
const fs = require('fs');let result = fs.existsSync('./writeFile.txt');
console.log("result", result); // result false
- 该操作的同步方法已被弃用
stat[Sync]
fs.stat(path[, options], callback)
:获取文件状态
fs.statSync(path[, options])
:返回文件状态
-
path
:文件的路径 -
options
:对获取操作的设置bigint
:status
中的数值是否为 bigint,默认为false
-
callBack
:接收 2 个参数:err
:错误信息,默认为null
status
:状态对象fs.Stats
,该对象中保存了当前对象状态的相关信息。eg:大小size
、创建时间birthtime
…
- 调用
isDirectory
方法可以查看当前对象是否为目录 - 调用
isFile
方法可以查看当前对象是否为文件
const fs = require('fs');let syncStatus = fs.statSync('./newSync.txt');
console.log("syncStatus", syncStatus); // syncStatus Stats {...}
console.log("syncStatus.isDirectory()", syncStatus.isDirectory()); // false -- 是否为目录
console.log("syncStatus.isFile()", syncStatus.isFile()); // true -- 是否为文件fs.stat('./new.txt', (err, status) => {if (err) return console.log("err", err); // 如果该文件不存在,则报错console.log("status", status); // status Stats {...}console.log("status.isDirectory()", status.isDirectory()); // falseconsole.log("status.isFile()", status.isFile()); // true
});
truncate[Sync]
fs.truncate(path[, len], callback)
:截取文件内容
fs.truncateSync(path[, len])
:返回undefined
path
:文件的路径len
:将文件数据截到指定大小 (byte),默认为0
callback
:回调函数,接收 1 个参数err
,默认为null
const fs = require('fs');// 同步截取文件
fs.truncateSync('./newSync.txt', 5);// 异步截取文件
fs.truncate('./new.txt', 5,err => console.log("err", err)
);
- 注意这里,一个中文字符占 2 / 3 个字节,所以截取指定字节后,可能会出现乱码
watchFile
fs.watchFile(filename[, options][, listener])
:监听文件数据的变化
-
filename
:文件的路径 -
options
:配置对象-
bigint
:默认为false
-
persistent
:只要正在监视文件,进程是否应继续运行。默认为true
-
interval
:监听的间隔时间,默认为5007
-
-
listener
:回调函数,监听的文件被修改后调用,接收 2 个参数:-
current
:文件当前的状态,是 fs.status 对象 -
previous
:文件修改前的状态,也是 fs.status 对象
-
- 返回值:
fs.StatWatcher
对象,存储着一些监听的信息。eg:大小size
const fs = require('fs');let wf = fs.watchFile('new.txt', (current, previous) => {console.log('文件当前大小' + current.size);console.log('文件先前大小' + previous.size);
});
console.log("wf", wf);
watch
fs.watch(filename[, options][, listener])
:监听文件(夹)变化(改动数据 & 移动 / 重命名)
filename
:文件(夹)路径options
:配置对象encoding
:指定用于传给监听器的文件名的字符编码
如果只设置encoding
,可以直接写成 String 形式persistent
:只要正在监视文件,进程是否应继续运行。默认为true
recursive
:是应监视所有子目录,还是仅监视当前目录。默认为false
(仅在 macOS 和 Windows 上受支持)
listener
:回调函数,接收 2 个参数:eventType
:参数值为rename
/change
filename
:触发事件的文件(夹)名称
const fs = require('fs');fs.watch('./new.txt', (eventType, filename) => {console.log("eventType", eventType);console.log("filename", filename);
});
- 修改文件的数据时,会输出:
eventType change
filename new.txt
- 修改文件名 / 删除文件时,会输出:
eventType rename
filename new.txt
修改文件名 / 删除文件之后,监听事件失效
流式文件写入 & 读取
流式文件写入 / 读取适合操作大文件
流式写入
createWriteStream
① 创建可写流:
fs.createWriteStream(path[, options])
path
:文件路径options
:配置对象flags
:文件系统标志,默认值为'w'
encoding
:数据的编码格式,默认为utf8
- 返回值:WriteStream 对象
const fs = require('fs');
let ws = fs.createWriteStream('./WS.txt');
- 创建可写流后,可写流会自动开启,写入完成后需要手动关闭
写入 - write
② 写入数据:
ws.write(data)
data
:需要写入的数据
const fs = require('fs');
let ws = fs.createWriteStream('./WS.txt');let content = '需要写入的内容';
ws.write(content);
ws.write(' 追加的内容');
监听 - open & close 事件
③ 可以通过监听流的
open
和close
事件来监听流的开关
因为 open
和 close
操作都只会出现一次,所以这里使用 once()
绑定事件
const fs = require('fs');
let ws = fs.createWriteStream('./WS.txt');let content = '需要写入的内容';
ws.write(content);
ws.write(' 追加的内容');// 监听 open 事件
ws.once('open', _ => console.log('可写流打开了'));
// 监听 close 事件
ws.once('close', _ => console.log('可写流关闭了'));
- 可以看见,只打印了
可写流打开了
,因为可写流会自动开启,但不会自动关闭
关闭 - close / end
④ 关闭可写流:
ws.close([callBack])
/ws.end([callBack])
callBack
:回调函数,关闭可写流后调用;接收一个参数err
,默认为null
const fs = require('fs');
let ws = fs.createWriteStream('./WS.txt');let content = '需要写入的内容';
ws.write(content);
ws.write(' 追加的内容');ws.once('open', _ => console.log('可写流打开了'));// 调用 close 方法关闭可写流
ws.close(_ => console.log('可写流关闭了'));
流式读取
createReadStream
① 创建可读流:
fs.createReadStream(path)
path
:文件的路径
- 返回值:ReadStream 对象
const fs = require('fs');
let rs = fs.createReadStream('./WS.txt');
- 创建可读流后,可读流会自动开启。但此时还不会读取数据!
- 我们可以通过
rs.readableFlowing
查看数据是否已经在读取
null
-未读取、true
-正在读取、false
-读取完,已关闭
开启 - data 事件
② 给可读流绑定一个
data
事件,绑定完毕后,会自动开始读取数据。每次最多读取 65536 byte
回调函数接收一个参数 data
,data
就是可读流传出的数据(二进制数据)
const fs = require('fs');
let rs = fs.createReadStream('./WS.txt');rs.on('data', data => {console.log('开始读取数据');console.log("data", data);
});
- 读取完后,可读流会自动关闭
监听 open & close 事件
③ 可以通过监听流的
open
和close
事件来监听流的开关
因为 open
和 close
操作都只会出现一次,所以这里使用 once()
绑定事件
const fs = require('fs');
let rs = fs.createReadStream('./WS.txt');// 监听 open & close 事件
rs.once('open', _ => console.log('readStream open!'));
rs.once('close', _ => console.log('readStream close!'));rs.on('data', data => {console.log('开始读取数据');console.log("data", data);
});
配合可写流使用
const fs = require('fs');
let rs = fs.createReadStream('./rs.txt');
let ws = fs.createWriteStream('./ws.txt');// 监听 data 事件,开启可读流
rs.on('data', data => {console.log('开始读取数据');console.log("data", data + ""); // 打印可读流里面的数据ws.write(data); // 写入读取到的数据
});// 监听可读流
rs.once('open', _ => console.log('ReadStream open!'));
rs.once('close', _ => {console.log('ReadStream close!');ws.end(); // 可读流关闭后,手动关闭可写流
});// 监听可写流
ws.once('open', _ => console.log('WriteStream open!'));
ws.once('close', _ => console.log('WriteStream close!'));
管道 pipe
pipe()
:可以将可读流中的数据,直接传到可写流中
const fs = require('fs');
let rs = fs.createReadStream('./WS.txt');
let ws = fs.createWriteStream('./123.txt');rs.pipe(ws); // 开启可读流,写入读取到的数据,写入完毕后自动关闭可写流// 监听可读流
rs.once('open', _ => console.log('ReadStream open!'));
rs.once('close', _ => console.log('ReadStream close!'));// 监听可写流
ws.once('open', _ => console.log('WriteStream open!'));
ws.once('close', _ => console.log('WriteStream close!'));
同步文件写入
openSync
① 打开文件:
fs.openSync(path[, flags])
-
path
:文件路径 -
flags
:文件系统标志-
'r'
(read):读取数据。如果文件不存在,则报错(默认) -
'w'
(write):写入数据。如果文件不存在,则创建该文件;否则覆盖文件原内容 -
'a'
(add):追加数据。如果文件不存在,则创建该文件;否则追加内容
-
- 返回值:表示文件描述符的整数
fd
const fs = require('fs');// 打开文件
let fd = fs.openSync('new.txt', 'w');
console.log("fd"); // fd 3
writeSync
② 写入内容:
fs.writeSync(fd, data[, position[, encoding]])
fd
:表示文件描述符的整数data
:需要写入的内容position
:开始写入的位置(eg:2
→ 空两位再开始写数据)encoding
:写入的数据编码格式,默认为utf8
- 返回值:写入的字节数
- 如果
data
是普通的对象,则它必须具有自己的(不是继承的)toString
方法
const fs = require('fs');let fd = fs.openSync('hello.txt', 'w');// 写入数据
let len = fs.writeSync(fd, 'hello superman');
console.log("len", len); // len 14
closeSync
③ 关闭文件:
fs.closeSync(fd)
fd
:表示文件描述符的整数
const fs = require('fs');let fd = fs.openSync('hello.txt', 'w');
fs.writeSync(fd, 'hello superman');// 关闭文件
fs.closeSync(fd);
写入完成后,需要关闭文件,避免资源的浪费
异步文件写入
open
① 打开文件:
fs.open(path[, flags], callback)
-
path
:文件的路径 -
flags
:文件系统标志-
'r'
(read):读取数据。如果文件不存在,则报错(默认) -
'w'
(write):写入数据。如果文件不存在,则创建该文件;否则覆盖文件原内容 -
'a'
(add):追加数据。如果文件不存在,则创建该文件;否则追加内容
-
-
callBack
:回调函数,打开文件后调用,接收 2 个参数:-
err
:错误信息,默认为null
-
fd
:表示文件描述符的整数
-
const fs = require('fs');// 打开文件
fs.open('hello.txt', 'w',_ => console.log(arguments) // [Arguments] { '0': null, '1': 3 }
);
write
② 写入数据:
fs.write(fd, string[, position[, encoding]], callback)
-
fd
:表示文件描述符的整数 -
string
:需要写入的内容 -
position
:开始写入的位置(eg:2 → 空两位再开始写数据)一般不写 -
encoding
:写入的数据编码格式,默认为utf8
,一般不写 -
callBack
:回调函数,接收 3 个参数:-
err
:错误信息,默认为null
-
len
:写入的字节数 -
data
:写入的数据内容
-
const fs = require('fs');// 打开文件
fs.open('hello.txt', 'w', (err, fd) => {// 写入数据fs.write(fd, '异步写入的内容', _ => {console.log(arguments); // [Arguments] { '0': null, '1': 21, '2': '异步写入的内容' }});
});
close
③ 关闭文件:
fs.close(fd, callBack)
fd
:表示文件描述符的整数callBack
:回调函数,接收 1 个异常参数err
,默认为null
const fs = require('fs');// 打开文件
fs.open('hello.txt', 'w', function (err, fd) {if (err) return console.log("err", err);console.log("open");// 写入数据fs.write(fd, '异步写入的内容', err => {if (err) return console.log("err", err);console.log("write");// 关闭文件fs.close(fd, function (err) {if (err) return console.log("err", err);console.log('close');});});
});