fs模块导入
Node.js 同时支持 CommonJS 和 ES 模块系统(自 Node.js v12 以来)
// 两种模块导入方式
import * as fs from 'fs';// Es6:这种方式需要在package.json中配置"type": "module"
const fs = require('fs');// commonJs:如果你只有一个单文件js那么你还是要使用这种方式导入
是否使用ES6的导入方式,取决于你是否有一个配置文件,或者就使用require导入
fs的读写文件方法
fs提供的文件操作方法包含3中模式:回调式,阻塞式,promise式
这里使用stat()方法举例,
检查文件信息 stat()
const fs = require('fs');// commonJs
const txt = '../public/file.txt'
// 回调式
fs.stat(txt, (err, stats) => {
if(err){
console.log(err.message);
}
console.log(stats.isFile());
})
// 阻塞式
try {
const stats = fs.statSync(txt);
} catch (err) {
console.error(err);
}
console.log(stats.isFile());
// promise式
async function example() {
try {
const stats = await fs.stat(txt);
} catch (err) {
console.log(err);
}
console.log(stats.isFile());
}
example();
使用后面两种方法时需要使用try,catch来捕获错误,
这里要注意 :如果不存在txt路径的文件,会产生错误,这种错误式 被捕获的err提示的,并非式致命错误(直接终止程序),它会提示你没有这个文件,而后面对这个文件的操作也会无法执行,
读取文件内容 readFile()
// 读取文件, 读取文件内容,并返回一个字符串
fs.readFile(txt, 'utf8', (err, data) => {
if(err){
console.log(err.message);
}
console.log(data);
})
参数:文件路径,读取格式(默认式二进制流格式),回调参数,err错误信息,data读取的数据
同样的,readFile也支持三种模式:回调式,阻塞式,promise式,这里仅展示回调式
写入文件writeFile()
// 写入文件, 写入文件内容,
fs.writeFile(txt, 'hello world', (err) => {
if(err){
console.log(err.message);
}
console.log('写入成功');
})
参数:文件路径,写入的内容,回调参数,err错误信息
同样的,writeFile也支持三种模式:回调式,阻塞式,promise式,这里仅展示回调式
追加内容appendFile()
fs.appendFile(txt, 'hello world', (err) => {
if(err){
console.log(err.message);
}
console.log('追加成功');
})
参数:文件路径,写入的内容,回调参数,err错误信息
同样的,appendFile也支持三种模式:回调式,阻塞式,promise式,这里仅展示回调式
注意 writeFile会覆盖原内容,从起始位置开始写入,appendFile不覆盖原内容,从末尾位置开始写入
文件夹操作
判断一个文件夹是否存在
使用 fs.existsSync(),这里exists不推荐使用了,事实上,对于文件夹的操作都建议使用阻塞式,因为文件都必须在文件夹下操作,文件夹没有被获取的话,文件操作就无法正常执行
新建一个文件夹
使用 fs.mkdir() 或 fs.mkdirSync() 创建新文件夹
读取目录的内容
使用 fs.readdir() 或 fs.readdirSync() 读取目录的内容。
重命名文件夹
使用 fs.rename() 或 fs.renameSync() 重命名文件夹。
删除文件夹
使用 fs.rmdir() 或 fs.rmdirSync() 删除文件夹。
新建一个文件夹
这里展示如何新建一个文件夹,其他方法类似,想了解更多可以参考:在 Node.js 中使用文件夹 (nodejs.cn)
const fs = require('fs');
const path = "./public/";
try {
// 如果没有public文件夹则创建
if (!fs.existsSync(path)) {
fs.mkdirSync(path);
}
} catch (err) {
console.error(err);
}
封装一个setFile方法
了解了基本的读写操作后我们可以封装一个方法,
该函数接收一个文件名和一个文件内容作为参数,并将文件内容写入指定的文件中(没有则创建),路径是当前目录下新建一个public文件夹,并将文件至于public下面。
/**
* @param {文件名称} baseName
* @param {写入的内容} content
* @param {内容是否覆盖} b 默认为false,不覆盖
*/
setFile(baseName,content,b)
module.exports.setFile = function (baseName, content, b = false) {
// 如果没有public文件夹则创建
try {
if (!fs.existsSync(path)) {
fs.mkdirSync(path);
console.log("文件夹 public 创建成功");
}
} catch (err) {
console.log(err);
}
// 判断文件是否存在,在open之前判断,回调结果在open之后
fs.stat(path + baseName, (err) => {
if (err) {// 文件不存在,产生错误
// console.log(err);
console.log(`文件 ${baseName} 创建成功`);
}
})
if (!b) {
// 不覆盖内容
// fs.readFile(path + baseName, "utf-8",(err, data) => {
// if (err) {
// console.error(err);
// }
// // 读取出原来的内容拼接
// content = data + content;
// fs.writeFile(path + baseName, content, (err) => {
// if (err) {
// console.error(err);
// }
// console.log("写入成功");
// })
// })
fs.appendFile(path + baseName, content, (err) => {
if (err) {
console.error(err);
} else {
console.log("写入成功");
}
})
} else {
fs.writeFile(path + baseName, content, (err) => {
if (err) {
console.error(err);
} else {
console.log("写入成功");
}
})
}
}
在同目录下新建一个useSetFile.js导入这个方法
// 导出的模块是整个文件,需要通过属性引入使用里面的方法
const setFile = require('./setFile');
const file = 'file.txt';
const content = 'Hello, World!';
setFile.setFile(file, content);
在控制台执行node useSetFile.js
可以看到目录下多了一个public文件夹和file.txt文件,并且写入了两个hello,world!
数据流缓冲区读写---open,close,read,write方法
出来file后缀的文件操作方法,fs模块还有基于数据流的读写方法,关于二进制数据流可以参考:
js二进制数据,文件---ArrayBuffer,二进制数组_js arraybuffer()-CSDN博客
// 打开文件,'w+/a+' 获得读,写权限,文件流起始位置在开头/末尾(a相比于a+没有读取权限), 如果文件不存在则创建,读取文件后会覆盖原有文件
fs.open(txt, 'a', (err, fd) => {
if(err){
console.log(err.message);
}
// 关闭文件, 关闭文件后才能进行其他操作
fs.close(fd, (err) => {
if(err){
console.log(err.message);
}
console.log('文件关闭成功');
})
})
/**
* fd, 使用fs.open打开成功后返回的文件描述符
* buffer, 一个Buffer对象,v8引擎分配的一段内存
* offset, 整数,从缓存区中读取/写入时的初始位置,以字节为单位
* length, 整数,从缓存区中读取/写入数据的字节数
* position, 整数,写入/读取文件初始位置;
* callback(err, written, buffer), 写入操作执行完成后回调函数,written实际写入字节数,buffer被读取的缓存区对象
*/
fs.write(fd, buffer, offset, length, position, callback);
fs.read(fd, buffer, offset, length, position, callback);
整体的使用结构为
open->read/write->close
open开始拿到fd,fd为一个整数,表示打开文件返回的文件描述符,window中又称文件句柄
后续操作以fd为文件标志进行操作
const fs = require('fs');
const path = __dirname+"/public/buffer.txt";// __dirname 当前文件所在目录的绝对路径
const buffer = new Buffer.from("你好"); //每一个汉字utf8编码是3个字节,英文是1个字节
fs.open(path, 'a+', (err, fd) => {
fs.write(fd,buffer,0,6,0, (err) => {// 0,6,0的含义,0,6从buffer中读取6个字节,0是开始读取的位置,0是写入的位置,写入文件的起始位置
if(err){
console.log(err);
}else{
console.log("写入成功");
}
})
fs.read(fd,buffer,0,6,0, (err, bytes) => {// 0,6,0的含义,0,6从buffer中写入6个字节,0是开始写入的位置,0是读取的位置,读取文件的起始位置
if(err){
console.log(err);
}else{
if(bytes > 0){
console.log(buffer.slice(0, bytes).toString());//打印buffer中的内容
fs.read(fd,buffer,0,6,null, (err, bytes) => {// 再次读取fd中的内容时,不标记出读取位置,默认为上次读取结束的位置,'a+'表示每次将位置放置在文件末尾
if(err){
console.log(err);
}else{
if(bytes > 0){
console.log(buffer.slice(0, bytes).toString());
}
}
})
}
fs.close(fd, (err) => {//结束本次文件读写
if(err){
console.log(err);
}else{
console.log("关闭文件成功");
}
})
}
})
});
这种方式的逻辑更加精细,可以控制数据流缓存区的位置进行读写,
值得注意的是,read和write两个方法中的3个整数参数,前两个是指定给buffer数据流缓冲区的 ,最后一个是文件的位置,当你进行read读取时,是对文件进行读取,在将读取的内容写入buffer,
当你进行write写入时,是对buffer进行读取,在将读取的内容写入文件 ,两者正好相反但是参数格式一致
完整代码总结和目录结构展示
file.js
// 两种模块导入方式
// import * as fs from 'fs';// Es6:这种方式需要在package.json中配置"type": "module"
const fs = require('fs');// commonJs
const path = require('path');
const txt = '../public/file.txt'
const txtMsg = [
path.dirname(txt),// 父级文件夹
path.basename(txt),// 文件名,包括后缀
path.extname(txt) // 文件后缀(扩展名)
]
let f = 0 ;
txtMsg.forEach(item => {
console.log(item);
})
// 同一代码块的回调执行顺序是 fs.open -> fs.close ->fs.stat -> fs.writeFile -> fs.readFile 打开文件,关闭文件,查看文件,写入文件,读取文件
// 打开文件,'w+/a+' 获得读,写权限,文件流起始位置在开头/末尾(a相比于a+没有读取权限), 如果文件不存在则创建,读取文件后会覆盖原有文件
fs.open(txt, 'a', (err, fd) => {
if(err){
console.log(err.message);
}
f = fd;
console.log(fd);
})
// 读取文件, 读取文件内容,并返回一个字符串
fs.readFile(txt, 'utf8', (err, data) => {
if(err){
console.log(err.message);
}
console.log(data);
})
// 写入文件, 写入文件内容,
fs.writeFile(txt, 'hello world', (err) => {
if(err){
console.log(err.message);
}
console.log('写入成功');
})
// 追加文件内容
fs.appendFile(txt, 'hello world', (err) => {
if(err){
console.log(err.message);
}
console.log('追加成功');
})
// 关闭文件, 关闭文件后才能进行其他操作
fs.close(f, (err) => {
if(err){
console.log(err.message);
}
console.log('文件关闭成功');
})
// 文件信息
fs.stat(txt, (err, stats) => {
if(err){
console.log(err.message);
}
console.log(stats.isFile());
})
// 每次执行此文件,会先打开txt.txt文件,(此时内容已清空)关闭文件,写入内容,再读取文件内容
setFile.js
// 封装一个setFile函数,该函数接收一个文件名和一个文件内容作为参数,并将文件内容写入指定的文件中(没有则创建),路径是当前目录下新建一个public文件夹,并将文件至于public下面。
// setFile(baseName,content,b)
const fs = require('fs');
const path = "./public/";
/**
* @param {文件名称} baseName
* @param {写入的内容} content
* @param {内容是否覆盖} b 默认为false,不覆盖
*/
module.exports.setFile = function (baseName, content, b = false) {
// 如果没有public文件夹则创建
try {
if (!fs.existsSync(path)) {
fs.mkdirSync(path);
console.log("文件夹 public 创建成功");
}
} catch (err) {
console.log(err);
}
// 判断文件是否存在,在open之前判断,回调结果在open之后
fs.stat(path + baseName, (err) => {
if (err) {// 文件不存在,产生错误
// console.log(err);
console.log(`文件 ${baseName} 创建成功`);
}
})
if (!b) {
// 不覆盖内容
// fs.readFile(path + baseName, "utf-8",(err, data) => {
// if (err) {
// console.error(err);
// }
// // 读取出原来的内容拼接
// content = data + content;
// fs.writeFile(path + baseName, content, (err) => {
// if (err) {
// console.error(err);
// }
// console.log("写入成功");
// })
// })
fs.appendFile(path + baseName, content, (err) => {
if (err) {
console.error(err);
} else {
console.log("写入成功");
}
})
} else {
fs.writeFile(path + baseName, content, (err) => {
if (err) {
console.error(err);
} else {
console.log("写入成功");
}
})
}
}
useSetFile.js
// 导出的模块是整个文件,需要通过属性引入使用里面的方法
const setFile = require('./setFile');
const file = 'file.txt';
const content = 'Hello, World!';
setFile.setFile(file, content);
buffer.js
const fs = require('fs');
const path = __dirname+"/public/buffer.txt";// __dirname 当前文件所在目录的绝对路径
const buffer = new Buffer.from("你好"); //每一个汉字utf8编码是3个字节,英文是1个字节
fs.open(path, 'a+', (err, fd) => {
fs.write(fd,buffer,0,6,0, (err) => {// 0,6,0的含义,0,6从buffer中读取6个字节,0是开始读取的位置,0是写入的位置,写入文件的起始位置
if(err){
console.log(err);
}else{
console.log("写入成功");
}
})
fs.read(fd,buffer,0,6,0, (err, bytes) => {// 0,6,0的含义,0,6从buffer中写入6个字节,0是开始写入的位置,0是读取的位置,读取文件的起始位置
if(err){
console.log(err);
}else{
if(bytes > 0){
console.log(buffer.slice(0, bytes).toString());//打印buffer中的内容
fs.read(fd,buffer,0,6,null, (err, bytes) => {// 再次读取fd中的内容时,不标记出读取位置,默认为上次读取结束的位置,'a+'表示每次将位置放置在文件末尾
if(err){
console.log(err);
}else{
if(bytes > 0){
console.log(buffer.slice(0, bytes).toString());
}
}
})
}
fs.close(fd, (err) => {//结束本次文件读写
if(err){
console.log(err);
}else{
console.log("关闭文件成功");
}
})
}
})
});