文件管理
摘要
Node.js 可以用来搭建静态资源服务器,既然如此,就会涉及到关于文件路径,文件读写等各种操作。这一篇文章就简单总结下。献上API 文档可进行参照。
# 路径操作
在进行文件操作之前,我们需要了解一点 不要使用拼接字符串的方式来处理路径,所以首先需要了解下路径操作
关于路径操作,Node.js 提供了 path
模块,这里介绍常用的 API
- path.normalize() 格式化为一个正常路径
- path.join() 路径拼接
- path.resolve() 把相对路径解析成绝对路径
- path.relative() 把绝对路径解析成相对路径
- path.basename() 文件名
- path.extname() 文件拓展名
- path.dirname() 文件所在路径
- path.parse() 返回一个路径对象
- path.format() 与 path.parse 相反
# 文件操作
介绍完路径操作,来迈入文件操作的大门。对于文件操作,Node.js 提供了 fs
这个模块来对文件进行简单地读写。在 fs 模块中有同步方法和异步方法。以下介绍常用的 API。
- fs.readFile() 异步读取文件
- fs.readFileSync() 同步读取文件
- fs.writeFile() 写入文件
- fs.stat() 获取文件的详细信息,可以用来判断文件是否存在
- fs.rename() 重命名文件
- fs.unlink 删除文件
- fs.readdir() 读取目录
- fs.mkdir() 创建目录
- fs.rmdir() 删除目录
- fs.watch() 监听文件变化,常用
在使用 fs 模块时经常会陷入到中回调地狱,一般会使用 co,使用 promise,使用 async 等异步方法来解决,详情点击 这里
介绍完上面的方法后,熟悉后端的朋友会知道有两个关键性问题,一个是编码格式,另外一个是字节。
- 关于编码格式:fs 模块中,读写方法中的第二个参数,基本上可以用来写文件的编码方式,详情看下 API 文档就是了
- 关于字节,这就要涉及到 Node.js 中的另外两个概念:
stream
和Buffer
,也是这一篇文章中主要的内容。
# Buffer 操作
# 了解 Buffer
Buffer 类用于在 TCP 流或文件系统操作等场景中处理字节流。
- Buffer 用于处理二进制数据流
- 实例类似整数数组,大小固定
- C++ 代码在 V8 堆外分配物理内存
Buffer 算是个伪全局变量,简单介绍下 API
- buf = Buffer.alloc(num) 实例化为 buf
- Buffer.from(obj) 将对象二进制化
- Buffer.byteLength(obj) 返回字符串的实际字节长度
- Buffer.isBuffer() 判断是否为 buffer 类型
- Buffer.concat(arr) Buffer 中有一些数组的基本操作
- buf.length 获取长度
- buf.toString() 转换为 String 类型
- buf.fill(content,start,end) 填充 Buffer 数组
- buf.equals() 比较 Buffer 内容
- buf.indexOf() buf 中有一些数组的基本操作
使用 Buffer 出现的中文乱码问题,一般是 3 个字节表示一个汉字。
可以使用 string_decoder 解决,详情可以看下文档
# Buffer 示例
介绍完 API,来写一下简单的示例
// 创建一个长度为 10、且用 0 填充的 Buffer。
const buffer = Buffer.alloc(10);
console.log(buffer);
//写入缓存区
const len = buffer.write("ASDFGHJK");
console.log("写入的字节数:" + len);
console.log(buffer);
//从缓冲区读取数据
console.log(buffer.toString("ascii", 0, 5));
//转化为Json对象
const json = buffer.toJSON();
console.log(json);
//缓冲区合并
const buf = Buffer.from("输出的内容为:");
const contact = Buffer.concat([buf, buffer]);
console.log(contact);
console.log(contact.toString());
//缓冲区比较
var buffer1 = Buffer.from("ABCDE");
var buffer2 = Buffer.from("ABCD");
var result = buffer1.compare(buffer2);
if (result < 0) {
console.log(buffer1 + " 在 " + buffer2 + "之前");
} else if (result == 0) {
console.log(buffer1 + " 与 " + buffer2 + "相同");
} else {
console.log(buffer1 + " 在 " + buffer2 + "之后");
}
//拷贝缓冲区
//将 buf2 插入到 buf1 指定位置上,替换原本的位置
buffer2.copy(buffer1, 3);
console.log("拷贝后为:" + buffer1.toString()); //ABCAB
//缓冲区裁剪
console.log("裁剪的部分:" + buffer1.slice(1, 4));
console.log(
"但并没有影响buffer1本身" + buffer1 + "\t" + "长度仍然为:" + buffer1.length
);
# Stream 操作
# 了解 Stream
Stream
就是数据信息的一个传输集合。适合进行大文件和连续的传输文件块的处理。
简单来说,Stream 其实就是 Buffer 的一个更高级封装加另外实现。但是,Buffer 在分配完成读取所有数据后,才能进行使用,而 Stream 只要建立,当消费者需要使用时便可以使用。同时,数据可以进入缓冲区,而这个缓冲区,其实就是 Buffer。
对于 Stream 的使用,可以通过 require
来引入 stream
模块,也可以使用其他模块中的 stream 方法来进行使用。在 Stream 中有一个很重要的概念,那就是管道 pipe,通过 pipe
方法来处理数据的流向。另外 Stream 中可以进行事件监听。
Stream 有 4 种类型,一般都是使用前两种类型
- Readable: 可读流
- Writable: 可写流
- Duplex: 可读可写
- Tranform: 读写过程中处理
# Stream 示例
介绍完以上概念,通过 fs 模块来进行简单的示例。新建 input.txt
和 output.txt
两个文件
先向 input.txt
写入数据
var fs = require("fs");
var data = "测试数据的写入。";
// 创建一个可以写入的流,写入到文件 input.txt 中,每次写入的时候会覆盖原有的内容
var writeStream = fs.createWriteStream("input.txt");
// 使用 utf8 编码写入数据
writeStream.write(data, "utf-8");
// 标记文件末尾
writeStream.end();
// 处理流事件
writeStream.on("finish", function() {
console.log("写入完成");
});
writeStream.on("error", function(err) {
console.log(err.stack);
});
console.log("程序执行完毕!");
然后使用 pipe
方法,将 input.txt
的数据写入到 output.txt
中
var fs = require("fs");
var readStream = fs.createReadStream("input.txt");
var writeStream = fs.createWriteStream("output.txt");
readStream.setEncoding("utf-8");
// 可以监听 Stream 事件
readStream.on("end", function(data) {
console.log(data);
});
// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readStream.pipe(writeStream);
console.log("程序执行完毕");