设为首页 收藏本站
查看: 1210|回复: 0

[经验分享] NodeJS学习之文件操作

[复制链接]
累计签到:2 天
连续签到:1 天
发表于 2017-2-24 10:02:36 | 显示全部楼层 |阅读模式
NodeJS -- 文件操作

Buffer(数据块)
  JS语言自身只有字符串数据类型,没有二进制数据类型,因此NodeJS提供了一个与String对等的全局构造函数Buffer来提供对二进制数据的操作。除了可以读取文件得到Buffer的实例外,还能够直接构造,例如:



var bin = new Buffer([0x68, 0x65, 0x6c, 0x6c, 0x6f]);
  Buffer与字符串类似,除了可以用.length属性得到字节长度外,还可以使用[index]方式读取指定位置的字节,例:



bin[0]; // => 0x68;
  Buffer与字符串能够互相转化,例:



var str = bin.toString('utf-8'); // => 'hello'
var bin = new Buffer('hello','utf-8'); // =><Buffer 68 65 6c 6c 6f>
  Buffer与字符串一个重要区别:字符串是只读的,并且对字符串的任何修改得到的都是一个新字符串,原字符串保持不变。至于Buffer,更像是可以做指针操作的C语言数组。例可以使用[index]方式直接修改某个位置的字节:



bin[0] = 0x48;
  而.slice方法也不是返回一个新的Buffer,而更像是反回了指向原Buffer中间某个位置的指针:
DSC0000.png

  因此对.slice方法返回的Buffer的修改会作用于原Buffer。


为此若要拷贝一份Buffer,需要先创建一个新的Buffer,通过.copy把原Buffer的数据复制过去,类似于先申请新内存,再把已有内存的数据复制过去。

总之,Buffer将JS数据处理能力扩展到了任意二进制数据
Stream(数据流)


当内存中无法一次装下需要处理的数据时,或者一边读取一遍处理更加高效时,我们就需要用到数据流。NodeJS通过各种Stream来提供对数据流的操作,在大文件拷贝时,可以为数据来源创建一个只读数据流,例:


var rs = fs.createReadStream(pathName);
rs.on('data', function(chunk) {
doSomething(chunk);
});
rs.on('end', function() {
cleanUp();
});
  注:Stream基于事件机制工作,所有Stream实例都继承于NodeJS提供的EventEmitter
  上边的代码中data事件会源源不断地被触发,不管doSomething()函数是否处理得过来。代码可以继续作如下改造,以解决这个问题:



var rs = fs.createReadStream(src);
rs.on('data', function(chunk) {
rs.pause();
doSomething(chunk, function() {
rs.resume();
});
});
rs.on('end', function() {
cleanUp();
});

以上给doSomething()函数加上了回调函数,因此我们可以在处理数据前暂停数据读取,并在处理数据后继续读取数据。

此外,还可以为数据目标创建一个只写数据流,例:


var rs = fs.createReadStream(src);
var ws = fs.createWriteStream(dst);
rs.on('data', function(chunk) {
ws.write(chunk);
});
rs.on('end', function(chunk) {
ws.end():
});
  以上代码看起来就像是一个文件拷贝程序了,不过,依然存在写入速度跟不上读取速度,会导致缓存爆仓的问题,我们可以根据.write方法的返回值来判断传入的数据是写入目标了,还是临时放在了缓存了,并根据drain事件来判断什么时候只写数据流已经将缓存中的数据写入目标,可以传入下一个待写数据了:



var rs = fs.createReadStream(src);
var ws = fs.createWriteStream(dst);
rs.on('data', function(chunk) {
if(ws.write(chunk) === false) {
rs.pause();
}
});
rs.on('end', function() {
ws.end();
});
rs.on('drain', function() {
rs.resume();
});
  实现了数据从只读数据流到只写数据流的搬运,并包括了放爆仓控制,NodeJS直接提供了.pipe方法来做这件事,内部实现方式与上述代码类似

File System(文件系统)


NodeJS通过内置fs模块提供对文件的操作,fs模块提供的API基本上可以分为以下三类:

文件属性读写:

常用的有:fs.stat、fs.chmod、fs.chown等等

文件内容读写:

常用的有:fs.readFile、fs.readdir、fs.writeFile、fs.mkdir等等

底层文件操作:

常用的有:fs.open、fs.read、fs.write、fs.close等等

NodeJS最精华的异步IO模型在fs模块里有着充分的体现,例如上面提到的API都通过回调函数传递结果,例:


fs.readFile(pathName, function(err, data) {
if(err) {
// Deal with error.
} else {
// Deal with data
    }
});

基本上所有fs模块API回调参数都有两个,第一个参数在有错误发生时等于异常对象,第二个参数始终用于返回API方法执行结果。

此外,fs模块的所有异步API都有对应的同步版本,用于无法使用异步操作时,或同步操作更方便时。同步API除了方法名末尾多了一个Sync之外,异常对象的处理需要使用异常捕获
Path(路径)
  操作文件时难免与文件路径打交道,NodeJS提供了path内置模块来简化路径相关操作,并提升代码可读性。
  path.normalize
  将传入的路径转换为标准路径,具体讲的话,除了解析路径中的.与..之外,还能去掉多余的斜杠,如果程序需要使用路径作为某些数据的索引,但有允许用户随意输入路径时,就需要使用该方法保证路径的唯一性,例:



var cache = {};
function store(key, value) {
cache[path.normalize(key)] = value;
}
store('foo/bar', 1);
store('foo//baz//../bar', 2);
console.log(cache); // {'foo/bar': 2}
  注意:标准化之后的路径里的斜杠在Window系统下是\,而在linux系统下是/,若想保证任何系统都使用/作为路径分隔符的话,需要.replace(/\\/g, '/')再替换下标准路径
  path.join
  将传入的多个路径拼接为标准路径,该方法可避免手工拼接字符串的繁琐,并且能在不同系统下正确使用相应路径分隔符。例:



path.join('foo/', 'baz/', '../bar'); // => "foo/bar"
  path.extname
  可获取文件的扩展名,当我们需要根据不同文件扩展名做不同操作时,该方法就显得很好用,例:



path.extname('foo/bar.js'); // => ".js"
遍历目录


遍历目录是操作文件时一个常见需求,例,写一个程序用于查找指定目录下的所有js文件时,就需要遍历整个目录

递归算法

遍历目录一般使用递归算法,否则就较难写出简洁的代码

注:使用递归算法虽然简洁,但每次递归都会产生函数调用,在需要优先考虑性能时,需要把递归算法转化为循环算法,以减少函数调用次数

遍历算法

目录是一个树状结构,遍历时一般采用深度优先+先序遍历算法

同步遍历

结合算法,使用文件系统的同步API

异步遍历

结合算法,使用文件系统的异步API,实现会变得复杂,但原理相同
文本编码
  常用的文本编码有UTF8和GBK两种,并且UTF8还可能带有BOM,在读取不同编码的文本文件时,需要将文件内容转换为JS使用的UTF8编码字符串后才能正常处理
  BOM的移除
  BOM用于标记一个文件使用Unicode编码,其本身是一个Unicode字符(“\uFEFF”),位于文本文件头部,在不同Unicode编码下,BOM字符对应的二进制字节如下:
DSC0001.png

  因此我们可以根据文本文件头几个字节等于啥来判断文件是否包含BOM,以及使用哪种Unicode编码,但BOM并不属于文件的一部分,需要去掉,否则在某些应用场景下会有问题。以下代码实现了识别和去除UTF8 BOM的功能



function readText(pathName) {
var bin = fs.readFileSync(pathName);
if(bin[0] === 0xEF && bin[1] === 0xBB && bin[2] === 0xBF) {
bin = bin.slice(3);
}
return bin.toString('utf-8');
}

注:

1、如果不是很在意性能,可以使用fs模块的同步API,更容易理解

2、需要对文件读写做到字节级别的精细控制时,请使用fs模块文件底层操作API

3、不要使用拼接字符串的方式来处理路径,使用path模块

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-346482-1-1.html 上篇帖子: 十个最适合 Web 和 APP 开发的 NodeJS 框架 下篇帖子: 【nodejs】文件上传demo实现
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表