nodejs FileSystem 模块
nodejs 的文件模块 从读写两方面入手。
首先说明一下文件操作模式
‘r’ - 以读取模式打开文件。如果文件不存在则发生异常。
‘r+’ - 以读写模式打开文件。如果文件不存在则发生异常。
‘rs+’ - 以同步读写模式打开文件。命令操作系统绕过本地文件系统缓存。
这对 NFS 挂载模式下打开文件很有用,因为它可以让你跳过潜在的旧本地缓存。 它对 I/O 的性能有明显的影响,所以除非需要,否则不要使用此标志。
注意,这不会使 fs.open() 进入同步阻塞调用。 如果那是你想要的,则应该使用 fs.openSync()。
‘w’ - 以写入模式打开文件。文件会被创建(如果文件不存在)或截断(如果文件存在)。
‘wx’ - 类似 ‘w’,但如果 path 存在,则失败。
‘w+’ - 以读写模式打开文件。文件会被创建(如果文件不存在)或截断(如果文件存在)。
‘wx+’ - 类似 ‘w+’,但如果 path 存在,则失败。
‘a’ - 以追加模式打开文件。如果文件不存在,则会被创建。
‘ax’ - 类似于 ‘a’,但如果 path 存在,则失败。
‘a+’ - 以读取和追加模式打开文件。如果文件不存在,则会被创建。
‘ax+’ - 类似于 ‘a+’,但如果 path 存在,则失败
调用 fs模块
var fs = require("fs");
读文件
- readFile 读取文件函数
该函数是异步读取文件内容
/**
* 参数: filename , [options] , callback
* filename: 必选参数,一定是文件的具体路径
* [options]: 可选参数 两种凡方式 第一种 不使用flag 则直接"utf8" ,否则格式如下
* callback : 读取文件的回调函数 第一个默认是err , 只有两个参数的时候才会将内容显示出来(data 参数可选)
*/
fs.readFile(__dirname+'/log.txt',{flag:"r",encoding:"utf8"},function (err,data) {
//如果读取文件出错 则 err = true
if(err){
console.log("读取文件出错!");
console.error(err);
}else{
console.log("读取文件成功");
console.log(data);
}
})
readFile同步调用函数为readFileSync(filename,[options]),该函数直接返回 file内容(buffer),如果指定了options编码格式 则返回的内容是字符串。
2. 读取打开的文件 fs.open /fs.read
windows下文字描述符如下:
O_RDONLY: 0,
O_WRONLY: 1,
O_RDWR: 2,
S_IFMT: 61440,
S_IFREG: 32768,
S_IFDIR: 16384,
S_IFCHR: 8192,
S_IFLNK: 40960,
O_CREAT: 256,
O_EXCL: 1024,
O_TRUNC: 512,
O_APPEND: 8,
F_OK: 0,
R_OK: 4,
W_OK: 2,
X_OK: 1
fs.open(filename,flags,[mode],callback)
/**
* 先打开文件 然后再读取 open read 函数
* open(filename,flags,[mode],callback)
* filename:完整的文件名(必选参数)
* flags: 操作标识 r:以读方式打开,r+:以读写模式打开文件
* [modes],权限,777 表示任何用户读写可以执行 默认是0666 (只有文件已经存在才有效)
* callback 打开文件之后回调函数 有两个参数 第一个默认err, 第二个为整数 表示打开文件返回的文字描述符
*
*
*/
fs.open(__dirname+'/log.txt',"r","0666",function (err,fd) {
if(err){
console.error(err);
}else{
console.log(fd);
console.log(fs.constants); //得到所有的文字描述符
}
});
fs.read
函数参数
fs.read(fd,buffer,offset,length,position,callback);
/**
* fs.read(fd,buffer,offset,length,position,callback);
* fd:fs.open()返回的文件描述符
* buffer:数据将被分配的对象区域 ,由v8引擎分配的内存
* offset:向buffer开始写入的偏移量 以字节为单位 (buffer内存开始的位置)
* length:指定要读取的文件长度
* position:指定从文件中哪个位置开始读取文件 , 默认是从文件其实位置开始。 (读取文件开始的位置)
* callback:回调函数 (err,bytesRead,buffer) bytesRead 表示实际读取的字节数 buffer 表示被读取的对象
*/
fs.open(__dirname+"/log.txt","r","0666",function (err,fd) {
if(err){
console.error(err);
}else{
var buffer = new Buffer(255);
console.log(buffer.length);
//每个汉字的utf8编码是3个字节,英文的utf8编码是1个 下面的9 相当于只读取了3个汉字
fs.read(fd,buffer,0,9,0,function (err,bytesRead,buffer) {
if(err){
console.error(err);
}else{
console.log(bytesRead);
console.log(buffer.slice(0,bytesRead).toString());
fs.read(fd,buffer,0,9,null,function (err,bytesRead,buffer) {
console.log(bytesRead);
console.log(buffer.slice(0,bytesRead).toString());
})
}
})
}
});
写文件
- fs.writeFile
参数:fs.writeFile(filename,data,[options],callback)
/**
* filename:完整文件路径 如果文件已存在则替换文件内容 ,若文件不存在则创建一个同名文件并追加内容
* data:anything,在这里我使用的是buffer 如果 data 是一个 buffer,则忽略 encoding 选项。它默认为 'utf8'
* [options]:文件操作类型 读写 同步等等 还有文件的编码等
* callback:只err
*/
fs.writeFile(__dirname+'/write.txt',wbuffer,{encoding:"utf8",flag:"w"},(err)=>{
if(err){
console.error(err);
}else{
console.log("write success ");
}
})
//追加文件
fs.appendFile(__dirname+'/write.txt',wbuffer,{encoding:"utf8",flag:"a+"},(err)=>{
if(err){
console.error(err);
}else{
console.log("追加成功");
}
})
- fs.write()
参数fs.write(fd, buffer, offset, length, position, callback);
fs.open(__dirname+'/write.txt',"a+",(err,fd)=>{
if(err) {
console.error(err);
}else{
console.log("打开成功!");
fs.write(fd,wbuffer,0,12,0,(err,bytesWritten,buffer)=>{
if(err){
console.error(err);
}else{
console.log("写入成功");
console.log(bytesWritten); // 12
console.log(buffer.slice(0,bytesWritten).toString()); //写入文本
//刷新缓存区 fs.write写入文件时,操作系统是将数据读到内存,再将数据写到文件中,
//当数据读完并不代表数据已经写完,因为有一部分还可能在内存中的缓冲区中
//我们可以使用fs.fsync方法将内存中的数据写入文件 刷新内存缓冲区
fs.fsync(fd,(err)=>{ //可能不会执行
if(err){
console.error(err);
}else{
console.log("flush success");
}
});
fs.close(fd,(err)=>{if(err){console.error(err)}});
}
})
}
});
目录
创建目录
fs.mkdir(path,[mode],callback);
/**
* path:被创建的目录名 完整目录路径
* [mode]:创建的目录权限 默认是 777 可读可写
* callback
*/
fs.mkdir(__dirname+'/fsDir',777,(err)=>{
if(err){
console.error(err);
}else{
console.log("创建目录成功");
}
});
- 读取目录
fs.readdir(path,options,callback)
fs.stat() 用于检查一个文件是否存在 ,返回的 stat是 fs.Stat一个对象实例,提供如:isFile, isDirectory,isBlockDevice等方法及size,ctime,mtime等属性
/**
*
* path:目录路径
* options: encoding 默认utf8
* callback : err , files:返回该目录下所有的文件包括文件夹 但是不包括以.. . 开头的文件
*/
var path = require('path');
fs.readdir(__dirname+'/fsDir/',(err,files)=>{
if(err){
console.error(err);
}else{
console.log("读取目录成功");
files.forEach(function (file) {
console.log(__dirname+"/fsDir/"+file); // D:\git\node_demo/fsDir/123
//得到文件的路径 使用path.mormalize()规范化路径的分隔符
var filePath = path.normalize(__dirname+"/fsDir/"+file);//D:\git\node_demo\fsDir\123is:dir
//fs.stat() 检查一个文件是否存在 //fs.access
// stats:
fs.stat(filePath,function (err,stats) {
//stats确定
if(stats.isFile()){
console.log(filePath+'is:file');
}
if(stats.isDirectory()){
console.log(filePath+'is:dir');
}
});
});
}
});
- 目录是否存在
fs.exist(path,callback);
//exists=true/false
fs.exists(__dirname+'/fsDir',function (exists) {
if(exists){
console.log("目录存在");
}else{
console.log("目录不存在");
}
});
监视
- 文件监视
监视文件是否被修改
fs.watchFile(filename,[options],listener);
/**
* fs.watchFile(filename,[options],listener)
* filename:文件名 完整路径
* options: 可省略 如果不省略就应该是一个对象{},这个对象包含两个属性:
* persistent:表示当文件正再被监视时,进程是否应该继续运行
* interval: 表示指定文件夹应该每隔多少毫秒被轮询一次
* 默认值:persistent:true,interval:5007;
*listener:类型为function 指定文件发生变化时回调 有两个参数
* curr:fs.Stat 对象 表示修改后对象
* prev: fs.Stat 对象 表示修改前对象
*/
fs.watchFile(__dirname+'/fsDir/123.txt',{persistent:true,interval:2000},(curr, prev)=>{
if(Date.parse(prev.ctime) === 0){
console.log("文件被创建");
}else if(Date.parse(curr.ctime) ===0){
console.log("文件被删除")
}else if(Date.parse(curr.mtime) != Date.parse(prev.mtime)) {
console.log('文件有修改');
}
});
fs.watchFile(__dirname + '/fsDir/123.txt',{persistent:true,interval:2000},function (curr, prev) {
console.log("文件有修改");
})
取消监视文件
//取消对文件进行监视
//fs.unwatchFile(filename, [listener]);
/**
* filename, 完整路径及文件名;
* [listener], 要取消的监听器事件,如果不指定,则取消所有监听处理事件
*/
var listener = function (curr, prev) {
console.log('取消监视函数')
}
fs.unwatchFile(__dirname + '/fsDir/123.txt', listener);
流的操作
- 创建读取流
//fs.createReadStream(path, [options])
/**
* path 文件路径
* [options] flags:指定文件操作,默认'r',读操作;encoding,指定读取流编码;autoClose, 是否读取完成后自动关闭,默认true;start指定文件开始读取位置;end指定文件开始读结束位置
*/
var rs = fs.createReadStream(__dirname + '/test.txt', {start: 0, end: 2});
//open是ReadStream对象中表示文件打开时事件,
rs.on('open', function (fd) {
console.log('开始读取文件');
});
rs.on('data', function (data) {
console.log(data.toString());
});
rs.on('end', function () {
console.log('读取文件结束')
});
rs.on('close', function () {
console.log('文件关闭');
});
rs.on('error', function (err) {
console.error(err);
});
//暂停和回复文件读取;
rs.on('open', function () {
console.log('开始读取文件');
});
rs.pause();
rs.on('data', function (data) {
console.log(data.toString());
});
setTimeout(function () {
rs.resume();
}, 2000);
- 创建写入流
//fs.createWriteStream(path, [options])
/**
* path 文件路径
* [options] flags:指定文件操作,默认'w',;encoding,指定读取流编码;start指定写入文件的位置
*/
/* ws.write(chunk, [encoding], [callback]);
* chunk, 可以为Buffer对象或一个字符串,要写入的数据
* [encoding], 编码
* [callback], 写入后回调
*/
/* ws.end([chunk], [encoding], [callback]);
* [chunk], 要写入的数据
* [encoding], 编码
* [callback], 写入后回调
*/
var ws = fs.createWriteStream(__dirname + '/test.txt', {start: 0});
var buffer = new Buffer('我也喜欢你');
ws.write(buffer, 'utf8', function (err, buffer) {
console.log(arguments);
console.log('写入完成,回调函数没有参数')
});
//最后再写入的内容
ws.end('再见');
//使用流完成复制文件操作
var rs = fs.createReadStream(__dirname + '/test.txt')
var ws = fs.createWriteStream(__dirname + '/test/test.txt');
rs.on('data', function (data) {
ws.write(data)
});
ws.on('open', function (fd) {
console.log('要写入的数据文件已经打开,文件描述符是: ' + fd);
});
rs.on('end', function () {
console.log('文件读取完成');
ws.end('完成', function () {
console.log('文件全部写入完成')
});
});
//关于WriteStream对象的write方法返回一个布尔类型,当缓存区中数据全部写满时,返回false;
//表示缓存区写满,并将立即输出到目标对象中
//第一个例子
var ws = fs.createWriteStream(__dirname + '/test/test.txt');
for (var i = 0; i < 10000; i++) {
var w_flag = ws.write(i.toString());
//当缓存区写满时,输出false
console.log(w_flag);
}
//第二个例子
var ws = fs.createWriteStream(__dirname + '/test/untiyou.mp3');
var rs = fs.createReadStream(__dirname + '/test/Until You.mp3');
rs.on('data', function (data) {
var flag = ws.write(data);
console.log(flag);
});
//系统缓存区数据已经全部输出触发drain事件
ws.on('drain', function () {
console.log('系统缓存区数据已经全部输出。')
});
- 管道pipe实现流读写
//rs.pipe(destination, [options]);
/**
* destination 必须一个可写入流数据对象
* [opations] end 默认为true,表示读取完成立即关闭文件;
*/
var rs = fs.createReadStream(__dirname + '/test/Until You.mp3');
var ws = fs.createWriteStream(__dirname + '/test/untiyou.mp3');
rs.pipe(ws);
rs.on('data', function (data) {
console.log('数据可读')
});
rs.on('end', function () {
console.log('文件读取完成');
//ws.end('再见')
});
最后 本文是参见 明明三省的博客,简述了自己觉得重要的部分。