文件IO
所谓的IO,就是input/output,说到IO,就不得不提外设,IO是针对外设的,众所周知,计算机核心的五大部件有,运算器,控制器,存储器,输出设备,输入设备,IO设备指的就是输入设备和输出设备,包括磁盘,网卡,显卡,打印机等等
这里我们学的是文件IO,也就是操作磁盘的IO接口.
IO设备的速度一般都远远低于内存和CPU的速度,所以产生了IO接口来做缓冲
node给我们提供了一个fs模块来操作文件系统
除了require,所有的函数路径都是基于控制台的,和模块本身的路径无关,我已我们处理路径最好是用path模块的resolve先转换为绝对路径(__dirname是文件的路径)
fs.readFile()
读取文件的API,第一个参数为路径,我们一般使用绝对路径,需要使用path模块的resolve函数进行路径处理
第二个参数为配置文件,可以设置读取格式,第三个参数为回调函数
node里所有的回调函数参数都是两个,第一个为错误,第二个为数据
javascript
const fs = require("fs");
const path = require("path");
const filename = path.resolve(__dirname, "./myfiles/1.txt");
// fs.readFile(filename, "utf-8", (err, content) => {
// console.log(content);
// });
// Sync函数是同步的,会导致JS运行阻塞,极其影响性能
// 通常,在程序启动时运行有限的次数即可
// const content = fs.readFileSync(filename, "utf-8");
// console.log(content);
async function test() {
const content = await fs.promises.readFile(filename, "utf-8");
console.log(content);
}
test();fs模块所有的操作都是异步操作,所以都是回调模式,但是fs模块里有一个promises对象,提供了文件操作所有函数的promise方法,可以消除回调的问题,使用promises的方法后就不需要传入回调函数了
除此之外,fs模块还提供了同步文件操作的函数,只需要在函数名的后面加上Sync即可,例如fs.readFileSync函数,这个函数就是同步的,不需要传入回调函数,但会导致阻塞,不建议直接用,初始化操作可以用
fs.writeFile()
同样,这个函数也有writeFileSync和promises.writeFIle
这个函数用于写入文件,没有返回值
javascript
const fs = require("fs");
const path = require("path");
const filename = path.resolve(__dirname, "./myfiles/2.txt");
async function test() {
// await fs.promises.writeFile(filename, "阿斯顿发发放到发", {
// flag: "a" //追加内容
// });
const buffer = Buffer.from("abcde", "utf-8");
await fs.promises.writeFile(filename, buffer);
console.log("写入成功");
}
test();如果没有文件,那么会给我们新建目录,但如果连目录都不存在,那么直接报错
有了writeFile和readFile之后,我们就可以实验一个复制效果
javascript
const fs = require("fs");
const path = require("path");
async function test() {
const fromFilename = path.resolve(__dirname, "./myfiles/1.jpeg");
const buffer = await fs.promises.readFile(fromFilename);
const toFilename = path.resolve(__dirname, "./myfiles/1.copy.jpeg");
await fs.promises.writeFile(toFilename, buffer);
console.log("copy success!");
}
test();当然,fs模块里面其实有现成的copyAPI可以用,这里只是演示一下writeFile和readFile
fs.stat()
可以获取文件或者目录的状态信息,只需要传入一个路径即可,会返回stats对象
javascript
const fs = require("fs");
const path = require("path");
const filename = path.resolve(__dirname, "./myfiles/");
async function test() {
const stat = await fs.promises.stat(filename);
console.log(stat);
console.log("是否是目录", stat.isDirectory());
console.log("是否是文件", stat.isFile());
}
test();stat方法也可以对目录进行操作,当stat方法访问的是一个目录时,返回的stats对象的size属性为0,也就是说目录的文件大小一定为0,这是由于目录在操作系统内部也是一个文件,这个文件只记录一个指针,没有任何实际数据,所以大小为0
stats对象的主要属性和方法如下
- size 占用字节
- atime 上次访问时间
- mtime 上次内容被修改时间
- ctime 上次状态被修改时间
- birthtime 创建时间
- isDirectory() 是否为目录
- isFile() 是否为文件
fs.readdir()
传入一个目录路径,读取这个目录路径,返回一个数组,数组保存了目录下的字目录和子文件
javascript
const fs = require("fs");
const path = require("path");
const dirname = path.resolve(__dirname, "./myfiles/");
async function test() {
const pathes = await fs.promises.readdir(dirname);
console.log(pathes);
}
test();fs.mkdir()
创建一个目录
javascript
const fs = require("fs");
const path = require("path");
const dirname = path.resolve(__dirname, "./myfiles/1");
async function test() {
await fs.promises.mkdir(dirname);
console.log("创建目录成功");
}
test();fs.exists()
这个API已经被弃用了,现在使用stat来代替,这个API可以判断文件或路径是否存在,我们可以自己手写一个exists
由于stat函数如果访问不存在的目录或文件会报错,我们可以抓取这个错误,来实现exists
javascript
const fs = require("fs");
const path = require("path");
const dirname = path.resolve(__dirname, "./myfiles/3");
async function exists(filename) {
try {
await fs.promises.stat(filename);
return true;
} catch (err) {
if (err.code === "ENOENT") {
//文件不存在
return false;
}
throw err;
}
}
async function test() {
const result = await exists(dirname);
if (result) {
console.log("目录已存在,无需操作");
} else {
await fs.promises.mkdir(dirname);
console.log("目录创建成功");
}
}
test();fs.unlink()
删除文件,传入一个路径即可删除
练习:递归遍历目录
javascript
const fs = require("fs");
const path = require("path");
class File {
constructor(filename, name, ext, isFile, size, createTime, updateTime) {
this.filename = filename;
this.name = name;
this.ext = ext;
this.isFile = isFile;
this.size = size;
this.createTime = createTime;
this.updateTime = updateTime;
}
async getContent(isBuffer = false) {
if (this.isFile) {
if (isBuffer) {
return await fs.promises.readFile(this.filename);
} else {
return await fs.promises.readFile(this.filename, "utf-8");
}
}
return null;
}
async getChildren() {
if (this.isFile) {
//文件不可能有子文件
return [];
}
let children = await fs.promises.readdir(this.filename);
children = children.map(name => {
const result = path.resolve(this.filename, name);
return File.getFile(result);
});
return Promise.all(children);
}
static async getFile(filename) {
const stat = await fs.promises.stat(filename);
const name = path.basename(filename);
const ext = path.extname(filename);
const isFile = stat.isFile();
const size = stat.size;
const createTime = new Date(stat.birthtime);
const updateTime = new Date(stat.mtime);
return new File(filename, name, ext, isFile, size, createTime, updateTime);
}
}
async function readDir(dirname) {
const file = await File.getFile(dirname);
return await file.getChildren();
}
async function test() {
const dirname = path.resolve(__dirname, "./myfiles");
const result = await readDir(dirname);
const datas = await result[0].getChildren();
console.log(datas);
}
test();