使用nodeJS中的fs模块对文件及目录进行读写,删除,追加,等操作详解
fs概述
文件I/O是由简单封装的标准POSIX函数提供的。nodeJS中通过require('fs')使用fs模块。所有的方法都有异步和同步的形式。
异步形式始终以完成回调作为它最后一个参数。传给完成回调的参数取决于具体方法,但第一个参数总是留给异常。如果操作成功完成,则第一个参数会是null或undefined
//异步示例 varfs=require('fs'); fs.unlink('/tmp/hello',function(err){ if(err)throwerr; console.log('successfullydeleted/tmp/hello'); });
当使用同步形式时,任何异常都会被立即抛出。可以使用try/catch来处理异常,或让它们往上冒泡
//同步示例 varfs=require('fs'); fs.unlinkSync('/tmp/hello'); console.log('successfullydeleted/tmp/hello');
异步方法不保证执行顺序。所以下面的例子容易出错
fs.rename('/tmp/hello','/tmp/world',function(err){ if(err)throwerr; console.log('renamedcomplete'); }); fs.stat('/tmp/world',function(err,stats){ if(err)throwerr; console.log('stats:${JSON.stringify(stats)}'); });
fs.stat可能在fs.rename之前执行。正确的方法是把回调链起来
fs.rename('/tmp/hello','/tmp/world',function(err){ if(err)throwerr; fs.stat('/tmp/world',function(err,stats){ if(err)throwerr; console.log('stats:${JSON.stringify(stats)}'); }); });
推荐开发者使用这些函数的异步版本。同步版本会阻塞整个进程,直到它们完成(停止所有连接)
fs底层操作
1、打开文件fs.open(path,flags[,mode],callback)
参数如下:
path
| flags
| mode
设置文件模式(权限和sticky位),但只有当文件被创建时才有效。默认为0666,可读写 callback
该回调有两个参数(err错误,fd文件标识,与定时器标识类似)
flags可以是:
'r'-以读取模式打开文件。如果文件不存在则发生异常。
'r+'-以读写模式打开文件。如果文件不存在则发生异常。
'rs+'-以同步读写模式打开文件。命令操作系统绕过本地文件系统缓存。
'w'-以写入模式打开文件。文件会被创建(如果文件不存在)或截断(如果文件存在)。
'wx'-类似'w',但如果path存在,则失败。
'w+'-以读写模式打开文件。文件会被创建(如果文件不存在)或截断(如果文件存在)。
'wx+'-类似'w+',但如果path存在,则失败。
'a'-以追加模式打开文件。如果文件不存在,则会被创建。
'ax'-类似于'a',但如果path存在,则失败。
'a+'-以读取和追加模式打开文件。如果文件不存在,则会被创建。
'ax+'-类似于'a+',但如果path存在,则失败。
[注意]使用'rs+'模式不会使fs.open()进入同步阻塞调用。如果那是你想要的,则应该使用fs.openSync()
varfs=require('fs'); fs.open('a.txt','r',function(err,fs){ console.log(err);//null console.log(fs);//3 })
varfs=require('fs'); fs.open('b.txt','r',function(err,fs){ /* {Error:ENOENT:nosuchfileordirectory,open'D:\project\b.txt' atError(native) errno:-4058, code:'ENOENT', syscall:'open', path:'D:\\project\\b.txt'} */ console.log(err); console.log(fs);//undefined })
文件的回调函数中的第二个参数fd代表文件标识,与定时器标识类似,用于标识文件,且随着文件的打开顺序递增
varfs=require('fs'); fs.open('1.txt','r',function(err,fs){ console.log(fs);//3 }) fs.open('2.txt','r',function(err,fs){ console.log(fs);//4 })
fs.openSync(path,flags[,mode])
fs.open()的同步版本。返回一个表示文件描述符的整数
varfs=require('fs'); varresult=fs.openSync('1.txt','r'); console.log(result);//3
2、读取文件fs.read(fd,buffer,offset,length,position,callback)
参数如下:
fd
通过fs.open()方法返回的文件描述符 buffer
| 数据将被写入到buffer offset
buffer中开始写入的偏移量 length
指定要读取的字节数(整数) position
指定从文件中开始读取的位置(整数)。如果position为null,则数据从当前文件位置开始读取 callback
回调有三个参数(err,bytesRead,buffer)。err为错误信息,bytesRead表示读取的字节数,buffer为缓冲区对象
由于使用read()方法,会将文件内容读取buffer对象中,所以需要提前先准备一个buffer对象
varfs=require('fs'); fs.open('1.txt','r',function(err,fd){ if(err){ console.log('文件打开失败'); }else{ varbf=Buffer.alloc(5); fs.read(fd,bf,0,3,null,function(err,len,buffer){ console.log(err);//null console.log(len);//3 console.log(buffer);//}) } });
fs.readSync(fd,buffer,offset,length,position)
fs.read()的同步版本,返回bytesRead的数量
varfs=require('fs'); varfd=fs.openSync('1.txt','r'); varbf=Buffer.alloc(5); varresult=fs.readSync(fd,bf,0,3,null); console.log(result);//3
3、写入文件fs.write(fd,buffer,offset,length[,position],callback)
参数如下
fd
文件标识 buffer
| 要将buffer中的数据写入到文件中 offset
buffer对象中要写入的数据的起始位置 length
length是一个整数,指定要写入的字节数 position
指定从文件开始写入数据的位置的偏移量。如果typeofposition!=='number',则数据从当前位置写入 callback
回调有三个参数(err,written,buffer),其中written指定从buffer写入了多少字节
[注意]多次对同一文件使用fs.write且不等待回调,是不安全的。对于这种情况,强烈推荐使用fs.createWriteStream
当我们要对打开的文件进行写操作的时候,打开文件的模式应该是读写模式
varfs=require('fs'); fs.open('1.txt','r+',function(err,fd){ if(err){ console.log('文件打开失败'); }else{ varbf=Buffer.from('test'); fs.write(fd,bf,0,3,null,function(err,len,buffer){ console.log(err);//null console.log(len);//3 console.log(buffer);//}) } });
fs.write(fd,data[,position[,encoding]],callback)
该方法写入data到fd指定的文件。如果data不是一个Buffer实例,则该值将被强制转换为一个字符串
不同于写入buffer,该方法整个字符串必须被写入。不能指定子字符串,这是因为结果数据的字节偏移量可能与字符串的偏移量不同
fd
文件标识 data
| 要将string或buffer中的数据写入到文件中 position
指向从文件开始写入数据的位置的偏移量。如果typeofposition!=='number',则数据从当前位置写入 encoding
期望的字符串编码 callback
回调有三个参数(err,written,str),其中written指定从str写入了多少字节
varfs=require('fs'); fs.open('1.txt','r+',function(err,fd){ if(err){ console.log('文件打开失败'); }else{ fs.write(fd,'12345',function(err,len,str){ console.log(err);//null console.log(len);//5 console.log(str);//}) } });
fs.writeSync()
fs.write()的同步版本。返回写入的字节数
varfs=require('fs'); varfd=fs.openSync('1.txt','r+'); varbf=Buffer.alloc(5); varresult=fs.writeSync(fd,bf,0,3,null); console.log(result);//3
4、关闭文件fs.close(fd,callback)
一个文件被操作后,要及时将该文件关闭
参数如下:
fd-通过fs.open()方法返回的文件描述符。
callback-回调函数,没有参数。
varfs=require('fs'); fs.open('1.txt','r+',function(err,fd){ if(err){ console.log('文件打开失败'); }else{ fs.close(fd,function(err){ if(err){ console.log(err); } console.log("文件关闭成功"); }); } });
fs.closeSync(fd)
fs.close(fd,callback)的同步版本,返回undefined
varfs=require('fs'); varfd=fs.openSync('1.txt','r+'); fs.closeSync(fd);
File操作
上一部分介绍的都是些底层的操作,接下来将介绍一些更便捷的文件操作。使用下列方法的时候,不需要再打开和关闭文件,直接操作即可
1、写入文件
fs.writeFile(file,data[,options],callback)
异步的将数据写入一个文件,如果文件不存在则新建,如果文件原先存在,会被替换
参数如下:
file-文件名或文件描述符。
data-要写入文件的数据,可以是String(字符串)或Buffer(流)对象。
options-该参数是一个对象,包含{encoding,mode,flag}。默认编码为utf8,模式为0666,flag为'w'
callback-回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。
varfs=require('fs'); varfilename='1.txt'; fs.writeFile(filename,'hello',function(err){ console.log(err);//null })
fs.writeFileSync(file,data[,options])
fs.writeFile()的同步版本。返回undefined
varfs=require('fs'); varfilename='1.txt'; fs.writeFileSync(filename,'abc');
2、追加文件
fs.appendFile(filename,data,[options],callback)
异步地追加数据到一个文件,如果文件不存在则创建文件。data可以是一个字符串或buffer
参数如下
file-文件名或文件描述符。
data-要写入文件的数据,可以是String(字符串)或Buffer(流)对象。
options-该参数是一个对象,包含{encoding,mode,flag}。默认编码为utf8,模式为0666,flag为'w'
callback-回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。
varfs=require('fs'); varfilename='1.txt'; fs.appendFile(filename,'world',function(err){ console.log(err);//null })
fs.appendFileSync(file,data[,options])
fs.appendFile()的同步版本。返回undefined
varfs=require('fs'); varfilename='1.txt'; fs.appendFileSync(filename,'lalala');
3、读取文件
fs.readFile(file[,options],callback)
参数如下
file-文件名或文件描述符
options-该参数是一个对象,包含{encoding,flag}。默认编码为null,即如果字符编码未指定,则返回原始的buffer;flag默认为'r'
callback-回调函数,回调有两个参数(err,data),其中data是文件的内容(buffer对象),err是错误信息参数,在写入失败时返回
varfs=require('fs'); varfilename='1.txt'; fs.readFile(filename,function(err,data){ if(err){ console.log('文件读取失败'); }else{ console.log(data);//console.log(data.toString());//'abcworldlalala' } });
fs.readFileSync(file[,options])
fs.readFile的同步版本。返回file的内容
如果指定了encoding选项,则该函数返回一个字符串,否则返回一个buffer
varfs=require('fs'); varfilename='1.txt'; varresult=fs.readFileSync(filename); console.log(result);//console.log(result.toString());'abcworldlalala'
4、删除文件
fs.unlink(path,callback)
参数如下:
path-文件路径。
callback-回调函数,没有参数。
varfs=require('fs'); varfilename='1.txt'; fs.unlink(filename,function(err){ if(err){ returnconsole.log('删除失败'); } console.log("删除成功"); });
fs.unlinkSync(path)
fs.unlink(path,callback)的同步版本,返回值为undefined
varfs=require('fs'); varfilename='1.txt'; fs.unlink(filename);
5、重命名
fs.rename(oldPath,newPath,callback)
参数如下:
oldPath
| newPath
| callback
回调只有一个可能的异常参数
varfs=require('fs'); varfilename='a.txt'; fs.rename(filename,'2.new.txt',function(err){ console.log(err);//null })
fs.renameSync(oldPath,newPath)
fs.rename(oldPath,newPath,callback)的同步版本,返回undefined
varfs=require('fs'); varfilename='2.new.txt'; varresult=fs.renameSync(filename,'a.txt');
6、文件信息
fs.stat(path,callback)
fs.stat()执行后,会将stats类的实例返回给其回调函数。可通过stats类中的提供方法判断文件的相关属性
参数如下:
path-文件路径。
callback-回调函数,带有两个参数如:(err,stats),stats是fs.Stats对象
varfs=require('fs'); varfilename='a.txt'; fs.stat(filename,function(err,stats){ console.log(err);//null /* {dev:223576, mode:33206, nlink:1, uid:0, gid:0, rdev:0, blksize:undefined, ino:7599824371527537, size:0, blocks:undefined, atime:2017-06-03T14:18:15.370Z, mtime:2017-06-03T14:18:15.370Z, ctime:2017-06-03T16:32:05.776Z, birthtime:2017-06-03T14:18:15.370Z} */ console.log(stats); });
stats类中的方法有
stats.isFile() 如果是文件返回true,否则返回false。
stats.isDirectory()如果是目录返回true,否则返回false。
stats.isBlockDevice() 如果是块设备返回true,否则返回false。
stats.isCharacterDevice() 如果是字符设备返回true,否则返回false。
stats.isSymbolicLink() 如果是软链接返回true,否则返回false。
stats.isFIFO() 如果是FIFO,返回true,否则返回false。FIFO是UNIX中的一种特殊类型的命令管道。
stats.isSocket() 如果是Socket返回true,否则返回false。
varfs=require('fs'); varfilename='a.txt'; fs.stat(filename,function(err,stats){ console.log(stats.isFile());//true });
fs.statSync(path)
fs.stat(path,callback)方法的同步版本,返回一个fs.Stats实例
varfs=require('fs'); varfilename='a.txt'; varresult=fs.statSync(filename); /* {dev:223576, mode:33206, nlink:1, uid:0, gid:0, rdev:0, blksize:undefined, ino:7599824371527537, size:0, blocks:undefined, atime:2017-06-03T14:18:15.370Z, mtime:2017-06-03T14:18:15.370Z, ctime:2017-06-03T16:32:05.776Z, birthtime:2017-06-03T14:18:15.370Z} */ console.log(result);
7、监听
fs.watch(filename[,options][,listener])
该方法用于监视filename的变化,filename可以是一个文件或一个目录。返回的对象是一个fs.FSWatcher
参数如下
filename
| options
| persistent
指明如果文件正在被监视,进程是否应该继续运行。默认为true recursive
指明是否全部子目录应该被监视,或只是当前目录。适用于当一个目录被指定时,且只在支持的平台。默认为false encoding
指定用于传给监听器的文件名的字符编码。默认为'utf8' listener
回调函数有两个参数(eventType,filename)。eventType可以是'rename'或'change',filename是触发事件的文件的名称
回调中提供的filename参数仅在Linux和Windows系统上支持。即使在支持的平台中,filename也不能保证提供。因此,不要以为filename参数总是在回调中提供,如果它是空的,需要有一定的后备逻辑
fs.watch('somedir',(eventType,filename)=>{ console.log(`事件类型是:${eventType}`); if(filename){ console.log(`提供的文件名:${filename}`); }else{ console.log('未提供文件名'); } });
varfs=require('fs'); varfilename='1.txt'; fs.watch(filename,function(eventType,_filename){ console.log(eventType);//change if(_filename){ console.log(_filename+'发生了改变');//'1.txt发生了改变' }else{ console.log('...'); } })
[注意]当一个文件出现或消失在一个目录里时,'rename'也会被触发
fs目录操作
1、创建
fs.mkdir(path[,mode],callback)
参数如下:
path-文件路径。
mode-设置目录权限,默认为0777。
callback-回调函数,回调只有一个可能的异常参数
varfs=require('fs'); fs.mkdir('./1',function(err){ console.log(err);//null })
fs.mkdirSync(path[,mode])
fs.mkdir(path[,mode],callback)的同步版本,返回undefined
varfs=require('fs'); fs.mkdirSync('./2');
2、删除
fs.rmdir(path,callback)
参数如下:
path-文件路径。
callback-回调函数,回调只有一个可能的异常参数
varfs=require('fs'); fs.rmdir('./1',function(err){ console.log(err);//null })
fs.rmdirSync(path,callback)
fs.rmdir(path,callback)的同步版本,返回undefined
varfs=require('fs'); fs.rmdirSync('./2');
3、读取
fs.readdir(path[,options],callback)
参数如下:
path
| options
| encoding
默认='utf8' callback
回调有两个参数(err,files),其中files是目录中不包括'.'和'..'的文件名的数组
varfs=require('fs'); fs.readdir('./',function(err,data){ console.log(err);//null /* ['.csslintrc', '.jshintrc', 'a.txt', 'dist', 'Gruntfile.js', 'Gruntfile1.js', 'index.html', 'main.js', 'node_modules', 'package.json', 'src'] */ console.log(data); })
varfs=require('fs'); fs.readdir('./',function(err,data){ data.forEach(function(item,index,arr){ fs.stat(item,function(err,stats){ if(stats.isFile()){ console.log('文件:'+item); } if(stats.isDirectory()){ console.log('目录:'+item); } }); }) }) /* 文件:.jshintrc 文件:.csslintrc 目录:dist 文件:Gruntfile.js 文件:index.html 文件:Gruntfile1.js 文件:main.js 目录:node_modules 文件:package.json 文件:a.txt 目录:src */
fs.readdirSync(path[,options],callback)
fs.readdir(path[,options],callback)的同步版本,返回一个不包括'.'和'..'的文件名的数组
varfs=require('fs'); varresult=fs.readdirSync('./'); /* ['.csslintrc', '.jshintrc', 'a.txt', 'dist', 'Gruntfile.js', 'Gruntfile1.js', 'index.html', 'main.js', 'node_modules', 'package.json', 'src'] */ console.log(result);
遍历目录
遍历目录是操作文件时的一个常见需求。比如写一个程序,需要找到并处理指定目录下的所有JS文件时,就需要遍历整个目录
遍历目录时一般使用递归算法,否则就难以编写出简洁的代码。递归算法与数学归纳法类似,通过不断缩小问题的规模来解决问题
functionfactorial(n){ if(n===1){ return1; }else{ returnn*factorial(n-1); } }
上边的函数用于计算N的阶乘(N!)。可以看到,当N大于1时,问题简化为计算N乘以N-1的阶乘。当N等于1时,问题达到最小规模,不需要再简化,因此直接返回1
目录是一个树状结构,在遍历时一般使用深度优先+先序遍历算法。深度优先,意味着到达一个节点后,首先接着遍历子节点而不是邻居节点。先序遍历,意味着首次到达了某节点就算遍历完成,而不是最后一次返回某节点才算数。因此使用这种遍历方式时,下边这棵树的遍历顺序是A>B>D>E>C>F
A
/\
B C
/\ \
D E F
了解了必要的算法后,我们可以简单地实现以下目录遍历函数
functiontravel(dir,callback){ fs.readdirSync(dir).forEach(function(file){ varpathname=path.join(dir,file); if(fs.statSync(pathname).isDirectory()){ travel(pathname,callback); }else{ callback(pathname); } }); }
可以看到,该函数以某个目录作为遍历的起点。遇到一个子目录时,就先接着遍历子目录。遇到一个文件时,就把文件的绝对路径传给回调函数。回调函数拿到文件路径后,就可以做各种判断和处理。因此假设有以下目录
-/home/user/
-foo/
x.js
-bar/
y.js
z.css
使用以下代码遍历该目录时,得到的输入如下
travel('/home/user',function(pathname){ console.log(pathname); }); ------------------------ /home/user/foo/x.js /home/user/bar/y.js /home/user/z.css
如果读取目录或读取文件状态时使用的是异步API,目录遍历函数实现起来会有些复杂,但原理完全相同。travel函数的异步版本如下
functiontravel(dir,callback,finish){ fs.readdir(dir,function(err,files){ (functionnext(i){ if(i更多关于nodeJS中fs模块对文件及目录进行读写,删除,追加,等操作的相关文章大家可以查看下面的相关链接
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。