Node.js-fs模块
文件写入
- 异步写入文件(fs.writeFile())
//语法:fs.writeFile(file, data[, options], callback)
//参数:文件路径、数据、选项(可选)、回调函数(err)const fs = require('fs');fs.writeFile('file.txt', 'Hello Node.js', function (err) {if (err) throw err;console.log('写入成功!');})
- 同步写入文件(fs.writeFileSync())
//阻塞主线程直至写入完成
//语法:fs.writeFileSync(file, data[, options])
//参数:文件路径、数据、选项(可选)const fs = require('fs');fs.writeFileSync('file.txt', 'Hello Node.js');
console.log('写入成功!');
Node.js 中的磁盘操作是由其他 线程 完成的,结果的处理有两种模式:
-
同步处理 JavaScript 主线程 会等待 其他线程的执行结果,然后再继续执行主线程的代码,
效率较低 -
异步处理 JavaScript 主线程 不会等待 其他线程的执行结果,直接执行后续的主线程代码,
效率较好 -
追加写入 (appendFile)
//语法:fs.appendFile(file, data[, options], callback)
//参数:文件路径、数据、选项(可选)、回调函数(err)const fs = require('fs');fs.appendFile('file.txt', '\nHello Node.js', function (err) {if (err) throw err;console.log('追加写入成功!');})
- 追加写入 (appendFileSync)
//语法:fs.appendFileSync(file, data[, options])
//参数:文件路径、数据、选项(可选)const fs = require('fs');fs.appendFileSync('file.txt', '\nHello Node.js');
console.log('追加写入成功!');
- 流式写入文件(writeStream)
// 分块写入大文件,减少内存占用:
//语法:fs.createWriteStream(path[, options])
//参数:文件路径、选项(可选)const fs = require('fs');const ws = fs.createWriteStream('file.txt');
ws.write('Hello Node.js');
ws.end();
文件读取
- 异步读取文件(fs.readFile())
// 非阻塞读取文件内容:
//语法:fs.readFile(path[, options], callback)
//参数:文件路径、选项(可选)、回调函数(err, data)const fs = require('fs');fs.readFile('file.txt', function (err, data) {if (err) throw err;console.log(data.toString());
})fs.readFile('file.txt', 'utf8', (err, data) => {if (err) throw err;console.log(data);
});
- 同步读取 (fs.readFileSync())
// 阻塞读取文件内容:
//语法:fs.readFileSync(path[, options])
//参数:文件路径、选项(可选)
const fs = require('fs');
const data = fs.readFileSync('file.txt', 'utf8');
console.log(data);
- 流式读取 (createReadStream)
//逐块读取大文件://语法:fs.createReadStream(path[, options])const rs = fs.createReadStream('file.txt', 'utf8');rs.on('data', (chunk) => {console.log('数据块:', chunk);});rs.on('end', () => console.log('读取完成'));
文件移动与重命名
在 Node.js 中,我们可以使用 rename 或 renameSync 来移动或重命名 文件或文件夹
// 语法:fs.rename(oldPath, newPath, callback)
//参数:旧路径、新路径、回调函数(err)
const fs = require('fs');// 重命名
fs.rename('file.txt', 'newFile.txt', (err) => {if (err) throw err;console.log('文件重命名成功');
});// 移动文件到新目录
// 目标目录必须存在,否则报错
fs.rename('newFile.txt', 'newdir/newFile.txt', (err) => {if (err) throw err;
});
文件删除
在 Node.js 中,我们可以使用 unlink 或 unlinkSync 来删除 文件或文件夹
// 语法:fs.unlink(path, callback)
//参数:文件路径、回调函数(err)
const fs = require('fs');// 删除文件
fs.unlink('file.txt', (err) => {if (err) throw err;console.log('文件删除成功');
})
// 语法:fs.unlinkSync(path)
fs.unlinkSync('./file.txt');// Node.js ≥14.4 (推荐)
fs.rm('file.txt', { recursive: true }, err => {});
文件夹操作
- 创建文件夹 (mkdir)
// fs.mkdir(path[, options], callback)
// fs.mkdirSync(path[, options])
// path 文件夹路径,options 选项配置( 可选 ),callback 操作后的回调// 如果文件夹不存在,则创建文件夹,否则报错
const fs = require('fs');
fs.mkdir('newdir', (err) => {if (err) throw err;console.log('文件夹创建成功');
});// 递归创建多层目录
fs.mkdir('dir1/dir2', { recursive: true }, (err) => {});//递归同步创建文件夹
fs.mkdirSync('x/y/z', {recursive: true});
- 读取文件夹内容 (readdir)
- 在 Node.js 中,我们可以使用 readdir 或 readdirSync 来读取文件夹
fs.readdir(path[, options], callback)
fs.readdirSync(path[, options])
// path 文件夹路径 // options 选项配置( 可选 )callback 操作后的回调//异步读取
fs.readdir('./newdir', (err, data) => {
if(err) throw err;
console.log(data);
});
//同步读取
let data = fs.readdirSync('./newdir');
console.log(data);
- 删除文件夹( rmdir )
在 Node.js 中,我们可以使用 rmdir 或 rmdirSync 来删除文件夹
fs.rmdir(path[, options], callback)
fs.rmdirSync(path[, options])//path 文件夹路径 options 选项配置( 可选 ) callback 操作后的回调// 删除空文件夹
fs.rmdir('emptydir', (err) => {});//同步递归删除文件夹
fs.rmdirSync('newdir', {recursive: true})// 递归删除非空文件夹 (推荐)
fs.rm('./dir1/dir1', { recursive: true }, (err) => {});
fs.rm('mydir', { recursive: true }, (err) => {});
- 查看资源状态
在 Node.js 中,我们可以使用 stat 或 statSync 来查看资源的详细信息
fs.stat(path[, options], callback)
fs.statSync(path[, options])
// path 资源路径 // options 选项配置( 可选 ) callback 操作后的回调
// 异步
// size文件体积
// birthtime 创建时间
// mtime最后修改时间
// isFile 检测是否为文件
// isDirectory 检测是否为文件夹
fs.stat('file.txt', (err, stats) => {
if(err) throw err;console.log(stats.isFile()); // trueconsole.log(stats.size); // 文件大小(字节)console.log(stats.mtime); // 修改时间
})
相对路径问题
fs 模块对资源进行操作时,路径的写法有两种:
- 相对路径
- /file.txt 当前目录下的file.txt
- file.txt 等效于上面的写法
- ./file.txt 当前目录的上一级目录中的file.txt
- 绝对路径
- D:/Program Files windows 系统下的绝对路径
- usr/bin Linux 系统下的绝对路径
相对路径中所谓的 当前目录 ,指的是 命令行的工作目录 ,而并非是文件的所在目录
所以当命令行的工作目录与文件所在目录不一致时,会出现一些 BUG
__dirname
__dirname 与 require 类似,都是 Node.js 环境中的’全局’变量
__dirname 保存着 当前文件所在目录的绝对路径 ,可以使用 __dirname 与文件名拼接成绝对路径
使用 fs 模块的时候,尽量使用 __dirname 将路径转化为绝对路径,这样可以避免相对路径产生的
Bug
文件复制方法
fs.copyFile() 是Node.js 文件系统模块提供的异步文件复制方法,用于高效地将源文件复制到目标路径。
const fs = require('fs');
fs.copyFile(src, dest[, flags], callback);
//阻塞线程直至操作完成
fs.copyFileSync()
// src:源文件路径(字符串、Buffer 或 URL)。
// dest:目标文件路径(复制后文件的新位置)。
// flags:可选,指定复制行为的标志(默认为 0,即覆盖目标文件)。
// COPYFILE_EXCL :目标文件存在时失败
// COPYFILE_FICLONE:尝试创建“写时复制”链接
// COPYFILE_FICLONE_FORCE:强制使用“写时复制”
// callback:回调函数,接收一个 err 参数,处理复制结果。fs.copyFile('source.txt', 'target.txt', fs.constants.COPYFILE_EXCL, (err) => {if (err) throw err;console.log('复制成功(目标文件不存在时才执行)');
});fs.copyFile('source.jpg', 'backup.jpg', (err) => {if (err) console.error('复制失败:', err);else console.log('备份完成!');
});try {fs.copyFileSync('source.txt', 'target.txt', fs.constants.COPYFILE_EXCL);
} catch (err) {if (err.code === 'EEXIST') console.log('文件已存在');
}
流式文件复制
在 Node.js 中,rs.pipe(ws) 是流(Stream)处理的核心方法,用于将可读流(Readable Stream)的数据直接传输到可写流(Writable Stream)。其作用本质是高效、自动地处理数据流动,尤其在处理大文件或实时数据时至关重要
- 自动数据传递
rs.pipe(ws) 会将可读流 rs 产生的数据块(chunks)自动传输到可写流 ws 中,无需手动监听 data 事件或循环读取数据
const fs = require('fs');
const rs = fs.createReadStream('source.txt');
const ws = fs.createWriteStream('copy.txt');rs.pipe(ws); // 自动将可读流的数据传输到可写流
- 背压控制
- 当写入流处理速度慢于读取流时(如目标磁盘性能不足),pipe() 会自动暂停读取流(rs.pause()),待写入流触发 drain 事件后再恢复读取(rs.resume()),避免内存溢出
- 当写入流处理速度快于读取流时,pipe() 会自动调节读取流的速度,以匹配写入流的处理能力
- 事件自动绑定
- pipe() 内部自动处理以下事件:
- data:读取数据块并写入目标文件。
- end:结束写入流(ws.end())。
- error:统一传递错误到目标流
const fs = require('fs');
const rs = fs.createReadStream('source.mp4');
const ws = fs.createWriteStream('copy.mp4');
const stats = fs.statSync('source.mp4');
const totalSize = stats.size;
let copiedSize = 0;rs.on('data', (chunk) => {copiedSize += chunk.length;const percent = Math.round((copiedSize / totalSize) * 100);console.log(`复制进度: ${percent}%`);
});rs.pipe(ws); // 保持管道连接
完整案例
const fs = require('fs');
const path = require('path');// 安全路径处理
const filePath = path.join(__dirname, 'data', 'file.txt');
console.log('文件路径:', filePath);
// 写入文件
fs.writeFile(filePath, 'Hello World', err => {if (err) return console.error('写入失败', err);// 读取文件fs.readFile(filePath, 'utf-8', (err, data) => {console.log('文件内容:', data);// 追加内容fs.appendFile(filePath, '\nNew content', err => {// 查看文件状态fs.stat(filePath, (err, stats) => {console.log('文件大小:', stats.size, 'bytes');});});});
});// 流式文件复制
const rs = fs.createReadStream('source.mp4');
const ws = fs.createWriteStream('copy.mp4');
rs.pipe(ws);