视频网站开发是什么,网站平台开发,台州关键词优化服务,网站地图深度做多少合适Buffer
JS 语言自身只有字符串数据类型#xff0c;没有二进制数据类型#xff0c;因此 NodeJS 提供了一个与 String 对等的全局构造函数 Buffer 来提供对二进制数据的操作。除了可以读取文件得到 Buffer 的实例外#xff0c;还能够直接构造#xff0c;Buffer 与字符串类似…Buffer
JS 语言自身只有字符串数据类型没有二进制数据类型因此 NodeJS 提供了一个与 String 对等的全局构造函数 Buffer 来提供对二进制数据的操作。除了可以读取文件得到 Buffer 的实例外还能够直接构造Buffer 与字符串类似除了可以用.length属性得到字节长度外还可以用[index]方式读取指定位置的字节。例如
var bin new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]); 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 中间的某个位置的指针如下所示。
[ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]^ ^| |bin bin.slice(2)
因此对.slice方法返回的 Buffer 的修改会作用于原 Buffer例如
var bin new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);
var sub bin.slice(2);sub[0] 0x65;
console.log(bin); // Buffer 68 65 65 6c 6f
也因此如果想要拷贝一份 Buffer得首先创建一个新的 Buffer并通过.copy方法把原 Buffer 中的数据复制过去。这个类似于申请一块新的内存并把已有内存中的数据复制过去。以下是一个例子。
var bin new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);
var dup new Buffer(bin.length);bin.copy(dup);
dup[0] 0x48;
console.log(bin); // Buffer 68 65 6c 6c 6f
console.log(dup); // Buffer 48 65 65 6c 6f
总之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 () {ws.end();
});
我们把 doSomething 换成了往只写数据流里写入数据后以上代码看起来就像是一个文件拷贝程序了。但是以上代码存在上边提到的问题如果写入速度跟不上读取速度的话只写数据流内部的缓存会爆仓。我们可以根据.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();
});ws.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 为例
fs.readFile(pathname, function (err, data) {if (err) {// Deal with error.} else {// Deal with data.}
});
如上边代码所示基本上所有 fs 模块 API 的回调参数都有两个。第一个参数在有错误发生时等于异常对象第二个参数始终用于返回 API 方法执行结果。
此外fs 模块的所有异步 API 都有对应的同步版本用于无法使用异步操作时或者同步操作更方便时的情况。同步 API 除了方法名的末尾多了一个 Sync 之外异常对象与执行结果的传递方式也有相应变化。同样以fs.readFileSync 为例
try {var data fs.readFileSync(pathname);// Deal with data.
} catch (err) {// Deal with error.
}
fs 模块提供的 API 很多这里不一一介绍需要时请自行查阅官方文档。
文件拷贝
NodeJS 提供了基本的文件操作 API但是像文件拷贝这种高级功能就没有提供因此我们先拿文件拷贝程序练手。与 copy 命令类似我们的程序需要能接受源文件路径与目标文件路径两个参数。
小文件拷贝
我们使用 NodeJS 内置的 fs 模块简单实现这个程序如下。
var fs require(fs);
function copy(src, dst) {fs.writeFileSync(dst, fs.readFileSync(src));
}
function main(argv) {copy(argv[0], argv[1]);
}
main(process.argv.slice(2));
以上程序使用 fs.readFileSync 从源路径读取文件内容并使用 fs.writeFileSync 将文件内容写入目标路径。
process 是一个全局变量可通过 process.argv 获得命令行参数。由于 argv[0] 固定等于 NodeJS 执行程序的绝对路径argv[1] 固定等于主模块的绝对路径因此第一个命令行参数从 argv[2] 这个位置开始。
大文件拷贝
上边的程序拷贝一些小文件没啥问题但这种一次性把所有文件内容都读取到内存中后再一次性写入磁盘的方式不适合拷贝大文件内存会爆仓。对于大文件我们只能读一点写一点直到完成拷贝。因此上边的程序需要改造如下。
var fs require(fs);
function copy(src, dst) {fs.createReadStream(src).pipe(fs.createWriteStream(dst));
}
function main(argv) {copy(argv[0], argv[1]);
}
main(process.argv.slice(2));
以上程序使用 fs.createReadStream 创建了一个源文件的只读数据流并使用 fs.createWriteStream 创建了一个目标文件的只写数据流并且用 pipe 方法把两个数据流连接了起来。连接起来后发生的事情说得抽象点的话水顺着水管从一个桶流到了另一个桶。