文件系统操作-fs
普通读取 readFileSync
const fs = require("fs");
let data;
try {
data = fs.readFileSync("./fileForRead.txt", "utf-8");
console.log("data", data);
} catch (err) {
console.log("错误了", err.message);
}
异步读取 readFile
const fs = require("fs");
fs.readFile("./fileForRead.txt", "utf-8", function (err, res) {
if (err) {
console.log("错误了", err.message);
}
console.log("文件内容", res);
});
通过文件流读取 readStream
成功了会走到 end 失败了不会走到 end
const fs = require("fs");
const readStream = fs.createReadStream("./fileForRead1.txt", "utf-8");
readStream
.on("data", function (res) {
console.log("读取数据", res);
})
.on("error", function (err) {
console.log("错误", err.message);
})
.on("end", function () {
console.log("没有数据了");
})
// 已经关闭,不会再有事件抛出
.on("close", function () {
console.log("已经关闭");
});
文件写入 asyncWrite
// 异步写入
function asyncWrite(params) {
const fs = require("fs");
fs.writeFile("./write.txt", "hello", "utf-8", function (err) {
if (err) throw err;
console.log("文件写入成功");
});
}
// 同步写入
function syncWrite(params) {
const fs = require("fs");
try {
fs.writeFileSync("./write.txt", "world", "utf-8");
console.log("如果成功");
} catch (error) {
console.log(error);
}
}
通过文件流写入 writeStream
const fs = require("fs");
const writeStream = fs.createWriteStream("./createWriteStream.txt", "utf-8");
writeStream.on("close", function () {
console.log("关闭了");
});
writeStream.write("hello");
writeStream.write("+");
writeStream.write("world");
writeStream.end("");
相对底层的接口
fs.write(fd, buffer, offset, length[, position], callback) fs.write(fd, data[, position[, encoding]], callback) fs.writeSync(fd, buffer, offset, length[, position]) fs.writeSync(fd, data[, position[, encoding]])
- fd:写入的文件句柄。
- buffer:写入的内容。
- offset:将 buffer 从 offset 位置开始,长度为 length 的内容写入。
- length:写入的 buffer 内容的长度。
- position:从打开文件的 position 处写入。
- callback:参数为
(err, written, buffer)。written表示有 xx 字节的 buffer 被写入。
备注:fs.write(fd, buffer, offset, length[, position], callback)跟fs.write(fd, data[, position[, encoding]], callback)的区别在于:后面的只能把所有的 data 写入,而前面的可以写入指定的 data 子串?
文件是否存在 access
fs.access() 除了判断文件是否存在(默认模式), 还可以判断文件的权限 fs.constants.F_OK 等常亮无法获取(node v6.1, mac 10.11.4 下, fs.constants 是 undefined)
const fs = require("fs");
fs.access("./aaa.txt", function (err) {
if (err) throw err;
console.log("文件存在");
});
创建目录 mkdir
// 异步
fs.mkdir("./aaa", function (err) {
if (err) throw err;
});
// 同步
fs.mkdirSync("./hello");
删除文件 unlink
// 异步
fs.unlink("./aaa.json", function (err) {
if (err) return err;
});
// 同步
fs.unlinkSync("./aaa.json");
遍历目录
// 同版本, 注意 fs.readdirSync() 只会读一层 所以需要判断文件类型是否为目录, 如果是, 则进行递归遍历
// path.resolve() 方法会把一个路径或路径片段的序列解析为一个绝对路径。
// fs.readdir() 方法将返回一个包含“指定目录下所有文件名称”的数组对象。
// fs.statSync 方法将返回一个文件实例
function getFilesInDir(dir) {
let reslut = [path.resolve(dir)];
const files = fs.readdirSync(dir, "utf-8");
files.forEach((file) => {
file = path.resolve(dir, file);
const stats = fs.statSync(file);
if (stats.isFile()) {
reslut.push(file);
} else if (stats.isDirectory()) {
reslut = reslut.concat(getFilesInDir(file));
}
});
console.log(reslut);
}
文件重命名 rename
fs.rename("./aaa", "./bbb", function (err) {
if (err) return err;
});
fs.renameSync("./bbb", "./ccc");
监听文件修改 watch watchFile
// fs.watch() fs.watchFile() 前者更加的高效
function watchFile(params) {
const options = {
persistent: true, // 默认为ttue
interval: 2000,
};
// curr prev 是监听文件的状态, fs.stat 实例
// 可以使用 fs.unwatch() 移除监听
// 访问文件也会触发这个方法 如果希望在文件被修改时得到通知,而不仅仅是访问,则需要比较curr。mtime和prev.mtime。
fs.watchFile("./write.txt", options, function (curr, prev) {
console.log("修改时间为:" + curr.atime);
});
fs.watch("./write.txt", options, function (event, filename) {
console.log("触发时间", event);
if (filename) {
console.log("文件名是", filename);
} else {
console.log("文件名是没有提供的");
}
});
}
修改所有者 chown
fs.chown();
fs.chmodSync();
fs.fchmod();
fs.fchmodSync();
修改权限 chmod
// fs.chmod() 与 fs.fchmod() 区别: 传的都是文件路径, 还是文件句柄
// fs.chmod() 与 fs.lchmod() 区别: 如果文件是软连接, 那么 fs.chmod() 修改的是软连接指向的目标文件 fs.lchmod() 修改的是软连接
fs.chmod(path, mode, callback);
fs.chmodSync(path, mode, callback);
fs.fchmod(fd, mode, callback);
fs.fchmodSync(fd, mode, callback);
fs.lchmod(path, mode, callback);
fs.lchmodSync(path, mode, callback);
获取文件状态 stat
function fileStats(params) {
// fs.stat() 和 fs.fstat() : 传文件路径 和 文件句柄
// fs.stat() 和 fs.lstat() : 如果文件是软连接, 那么fs.stat()返回目标文件的状态, fs.lstat()返回软连接本身的状态
fs.stat(path, callback);
fs.statSync(path);
fs.fstat(path, callback);
fs.fstatSync(path);
fs.lstat(path, callback);
fs.lstatSync(path);
stats.isFile(); // 是否是文件
stats.isDirectory(); // 是否是目录
stats.isBlockDevice(); // 检查特定Dirent是否描述了块设备
stats.isCharacterDevice(); // 检查特定Dirent是否描述了字符设备。
stats.isSymbolicLink(); // 用于检查fs.Stats对象是否描述符号链接。
stats.isFIFO(); // 检查fs.Stats对象是否描述了先进先出(FIFO)管道。
stats.isSocket(); // 是不是socket文件
}
function stat(params) {
fs.stat("./write.txt", function (err, stats) {
console.log(stats);
// size 文件大小
// birthtime 创建时间
// atime 访问时间
// mtime 修改时间
});
}
访问/权限检测 access
function access(params) {
// 异步
fs.access("./aaa.txt", function (err) {
if (err) return err;
console.log("可以访问");
});
// 同步
try {
fs.accessSync("./aaa.txt");
} catch (error) {
console.log("不可以访问");
}
}
文件读取(底层)read
// fd 文件句柄
// buffer 将读取的文件写到buffer里
// offset buffer开始写入的位置
// length 要读取的字节数
// position 文件从哪个位置开始读取 如果是null 那么就从当前位置开始读取
// 此外 callback 的回调参数为 (err, byteRead, buffer)
fs.read(fd, buffer, offset, length, position, callback);
追加文件内容 appendFile
file 可以是文件路径, 也可以是文件句柄
data 要追加的内容, string 或者 buffer
options
encoding 编码 默认是 utf-8
mode 默认是0o666
flag 默认是a
开始追加数据前 file 需要已经打开
file需要手动关闭
fs.appendFile('./aaa.txt', 'hello', 'utf-8', function (err) {
if (err) return err
console.log('写入成功');
})
文件内容截取 truncate
fs.truncateSync(path, len);
fs.ftruncate(fd, len, callback);
fs.ftruncateSync(fd, len);
// 要点:
// offset 不会改变, 比如通过 fs.read() 读取文件内容就需要特备注意
// 如果len小于文件内容长度 剩余文件内容部分丢失, 如果len大于文件内容长度, 那么超出的部分会用 \0 进行条虫
// 如果传入的是文件路径, 需要确保文件是可写的, 如果传的是文件句柄, 需要确保文件句柄已经打开并且可写入
fs.truncate("./aaa.txt", 22, function (err) {
if (err) return err;
console.log("截取成功");
});
修改文件属性(时间) utimes
// path/fd 文件路径/文件句柄
// atime 上一次访问文件数据的时间
// mtime 修改时间
fs.utimes(path, atime, mtime, callback);
fs.utimesSync(path, atime, mtime);
fs.futimes(fd, atime, mtime, callback);
fs.futimesSync(fd, atime, mtime);
// 通过stat查看文件的状态信息 包括上面的atime mtime
// 通过touch修改这几个时间
创建文件链接 symlink
fs.symlink(target, path, type, callback);
fs.symlinkSync(target, path, type);
fs.link(target, path, type, callback);
fs.lutimesSync(target, path, type);
// 硬链接: inode相同, 多个别名, 删除一个硬链接文件, 不会影响其他相同的inode的文件
// 软链接: 有自己的inode, 用户数据块存放指向文件的inode
创建临时目录 mkdtemp
// fs.mkdtemp(prefix, callback)
// fs.mkdirSync(prefix)
fs.mkdtemp("/tmp/", function (err, folder) {
if (err) return err;
console.log("创建目录成功", folder);
});
找到软链接指向的真实路径 readlink
const randomFileName = "./aaa-" + String(Math.random()).slice(2, 6) + ".txt";
fs.symlinkSync("./aaa.txt", randomFileName);
fs.readlink(randomFileName, "utf-8", function (err, linkString) {
if (err) return err;
console.log("链接文件内容", linkString);
});
真实链接 realpath
// fileForRealPath1.txt 是普通文件,正常运行
fs.realpath("./extra/inner/fileForRealPath1.txt", function (err, resolvedPath) {
if (err) throw err;
console.log("fs.realpath: " + resolvedPath);
});
// fileForRealPath.txt 是软链接, 会报错,提示找不到文件
fs.realpath("./extra/inner/fileForRealPath.txt", function (err, resolvedPath) {
if (err) throw err;
console.log("fs.realpath: " + resolvedPath);
});
删除目录 rmdir
fs.rmdir("./ccc", function (err) {
if (err) return err;
console.log("删除成功");
});
不常用
缓冲区内容写到磁盘
>fs.fdatasync(fd, callback)
>fs.fdatasyncSync(fd)
可以参考这里:
>1、sync函数
sync函数只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘操作结束。
通常称为update的系统守护进程会周期性地(一般每隔30秒)调用sync函数。这就保证了定期冲洗内核的块缓冲区。命令sync(1)也调用sync函数。
2、fsync函数
fsync函数只对由文件描述符filedes指定的单一文件起作用,并且等待写磁盘操作结束,然后返回。
fsync可用于数据库这样的应用程序,这种应用程序需要确保将修改过的块立即写到磁盘上。
3、fdatasync函数
fdatasync函数类似于fsync,但它只影响文件的数据部分。而除数据外,fsync还会同步更新文件的属性。
对于提供事务支持的数据库,在事务提交时,都要确保事务日志(包含该事务所有的修改操作以及一个提交记录)完全写到硬盘上,才认定事务提交成功并返回给应用层。