JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。
buffer中存储的是16进制,将node的2进制保存为16进制存储。一般情况下utf8编码是3个字节,gbk/gb2312是2个字节。
Buffer的使用
// 创建一个长度为 10、以零填充的 Buffer。
const buf1 = Buffer.alloc(10);
// 创建一个长度为 10 的 Buffer,
// 其中全部填充了值为 `1` 的字节。
const buf2 = Buffer.alloc(10, 1);
// 创建一个长度为 10、且未初始化的 buffer。
// 这个方法比调用 Buffer.alloc() 更快,
// 但返回的 Buffer 实例可能包含旧数据,
// 因此需要使用 fill()、write() 或其他能填充 Buffer 的内容的函数进行重写。
const buf3 = Buffer.allocUnsafe(10);
// 创建一个包含字节 [1, 2, 3] 的 Buffer。
const buf4 = Buffer.from([1, 2, 3]);
// 创建一个包含字节 [1, 1, 1, 1] 的 Buffer,
// 其中所有条目均使用 `(value & 255)` 进行截断以符合 0-255 的范围。
const buf5 = Buffer.from([257, 257.5, -255, '1']);
// 创建一个 Buffer,其中包含字符串 'tést' 的 UTF-8 编码字节:
// [0x74, 0xc3, 0xa9, 0x73, 0x74](以十六进制表示)
// [116, 195, 169, 115, 116](以十进制表示)
const buf6 = Buffer.from('tést');
// 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
const buf7 = Buffer.from('tést', 'latin1');
可以通过toString方法将Buffer对象转换为字符串,toString方法接收一个参数,用于指定转换成哪种类型的字符串。比如说utf8, base64等。
const buf = Buffer.from('hello world', 'utf8');
console.log(buf.toString('hex'));
// 打印: 68656c6c6f20776f726c64
console.log(buf.toString('base64'));
// 打印: aGVsbG8gd29ybGQ=
console.log(Buffer.from('fhqwhgads', 'utf8'));
// 打印: <Buffer 66 68 71 77 68 67 61 64 73>
console.log(Buffer.from('fhqwhgads', 'utf16le'));
// 打印: <Buffer 66 00 68 00 71 00 77 00 68 00 67 00 61 00 64 00 73 00>
使用以上方法之一将 Buffer 转换为字符串,称为解码;将字符串转换为 Buffer,称为编码。
-
'utf8': 多字节编码的 Unicode 字符。 许多网页和其他文档格式都使用 UTF-8。 这是默认的字符编码。 当将 Buffer 解码为不专门包含有效 UTF-8 数据的字符串时,则会使用 Unicode 替换字符 U+FFFD � 来表示这些错误
-
'utf16le': 多字节编码的 Unicode 字符。 与 'utf8' 不同,字符串中的每个字符都会使用 2 个或 4 个字节进行编码。 Node.js 仅支持 UTF-16 的小端序变体。
-
'latin1': Latin-1 代表 ISO-8859-1。 此字符编码仅支持从 U+0000 到 U+00FF 的 Unicode 字符。 每个字符使用单个字节进行编码。 超出该范围的字符会被截断,并映射成该范围内的字符。
Node.js 还支持以下两种二进制转文本的编码。 对于二进制转文本的编码,其命名约定是相反的:将 Buffer 转换为字符串通常称为编码,而将字符串转换为 Buffer 则称为解码。
-
'base64': Base64 编码。 当从字符串创建 Buffer 时,此编码也会正确地接受 RFC 4648 第 5 节中指定的 “URL 和文件名安全字母”。 base64 编码的字符串中包含的空格字符(例如空格、制表符和换行)会被忽略。
-
'hex': 将每个字节编码成两个十六进制的字符。 当解码仅包含有效的十六进制字符的字符串时,可能会发生数据截断。
-
'ascii': 仅适用于 7 位 ASCII 数据。 当将字符串编码为 Buffer 时,这等效于使用 'latin1'。 当将 Buffer 解码为字符串时,则使用此编码会在解码为 'latin1' 之前额外取消设置每个字节的最高位。 通常,当在编码或解码纯 ASCII 文本时,应该没有理由使用这种编码,因为 'utf8'(或者,如果已知的数据始终为纯 ASCII,则为 'latin1')会是更好的选择。 这仅为传统的兼容性而提供。
-
'binary': 'latin1' 的别名。 有关此编码的更多背景,请参阅二进制字符串。 该编码的名称可能会引起误解,因为此处列出的所有编码都是在字符串和二进制数据之间转换。 对于在字符串和 Buffer 之间进行转换,通常 'utf-8' 是正确的选择。
-
'ucs2': 'utf16le' 的别名。 UCS-2 以前是指 UTF-16 的一种变体,该变体不支持代码点大于 U+FFFF 的字符。 在 Node.js 中,始终支持这些代码点。
Buffer.from('1ag', 'hex');
// 打印 <Buffer 1a>,当遇到第一个非十六进制的值('g')时,则数据会被截断。
Buffer.from('1a7g', 'hex');
// 打印 <Buffer 1a>,当数据以一个数字('7')结尾时,则数据会被截断。
Buffer.from('1634', 'hex');
// 打印 <Buffer 16 34>,所有数据均可用。
现代的 Web 浏览器遵循 WHATWG 编码标准,将 'latin1' 和 'ISO-8859-1' 别名为 'win-1252'。 这意味着当执行 http.get() 之类的操作时,如果返回的字符集是 WHATWG 规范中列出的字符集之一,则服务器可能实际返回 'win-1252' 编码的数据,而使用 'latin1' 编码可能错误地解码字符
Buffer 与 TypedArray
这里简单介绍一下TypedArray,我们都知道,对于传统的web前端来说,是无法直接操作文件的,早起的浏览器只提供form表单的file-input标签用于上传文件。
ES6提供了,ArrayBuffer和TypedArray,让前端也可以直接操作编辑二进制数据,网页中的类型为file的input标签,也可以通过FileReader转化为二进制, 然后再做编辑等。
ArrayBuffer : 代表内存之中的一段二进制数据, 通过它我们可以直接创建二进制对象,然后使用相关的方法和属性。ArrayBuffer对象代表原始的二进制数据,他只能存储而不能直接进行编辑。
TypedArray是真正用来读写简单类型的二进制数据,Typed是一个泛指,表示的是具体哪种类型的数据,比如Int8Array, Uint8Array。
var ab = new ArrayBuffer(32)
var iA = new Int8Array(ab)
iA[0] = 97;//把二进制的数据的首位改为97 ,97为小写字母a的ascll码;
var blob = new Blob([iA], {type: "application/octet-binary"});//把二进制的码转化为blob类型
var url = URL.createObjectURL(blob);
Buffer 实例也是 JavaScript 的 Uint8Array 和 TypedArray 实例。 所有的 TypedArray 方法在 Buffer 上也可用。 但是, Buffer 的 API 和 TypedArray 的 API 之间存在细微的不兼容。
Buffer 与迭代器
Buffer 实例可以使用 for..of 语法进行迭代:
const buf = Buffer.from([1, 2, 3]);
for (const b of buf) {
console.log(b);
}
// 打印:
// 1
// 2
// 3
此外,buf.values()、buf.keys()、和 buf.entries() 方法也可用于创建迭代器。
Buffer.alloc(size[, fill[, encoding]])
-
size 新 Buffer 的期望长度。
-
fill | | | 用于预填充新 Buffer 的值。默认值: 0。
-
encoding 如果 fill 是一个字符串,则这是它的字符编码。默认值: 'utf8'。
分配一个大小为 size 字节的新 Buffer。 如果 fill 为 undefined,则用零填充 Buffer。
const buf = Buffer.alloc(5);
console.log(buf);
// 打印: <Buffer 00 00 00 00 00>
如果 size 大于 buffer.constants.MAX_LENGTH 或小于 0,则抛出 ERR_INVALID_OPT_VALUE。
如果指定了 fill,则分配的 Buffer 通过调用 buf.fill(fill) 进行初始化。
const buf = Buffer.alloc(5, 'a');
console.log(buf);
// 打印: <Buffer 61 61 61 61 61>
如果同时指定了 fill 和 encoding,则分配的 Buffer 通过调用 buf.fill(fill, encoding) 进行初始化 。
const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');
console.log(buf);
// 打印: <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
调用 Buffer.alloc() 可能比替代的 Buffer.allocUnsafe() 慢得多,但能确保新创建的 Buffer 实例的内容永远不会包含来自先前分配的敏感数据,包括可能尚未分配给 Buffer 的数据。
如果 size 不是一个数字,则抛出 TypeError。
Buffer.allocUnsafe(size)创建一个大小为 size 字节的新 Buffer。 如果 size 大于 buffer.constants.MAX_LENGTH 或小于 0,则抛出 ERR_INVALID_OPT_VALUE。
以这种方式创建的 Buffer 实例的底层内存是未初始化的。 新创建的 Buffer 的内容是未知的,可能包含敏感数据。 使用 Buffer.alloc() 可以创建以零初始化的 Buffer 实例。
Buffer 模块会预分配一个内部的大小为 Buffer.poolSize 的 Buffer 实例,作为快速分配的内存池,用于使用 Buffer.allocUnsafe() 创建新的 Buffer 实例、或 Buffer.from(array)、或 Buffer.concat()、或弃用的 new Buffer(size) 构造器但仅当 size 小于或等于 Buffer.poolSize >> 1(Buffer.poolSize 除以二再向下取整)。
对这个预分配的内部内存池的使用是调用 Buffer.alloc(size, fill) 和 Buffer.allocUnsafe(size).fill(fill) 两者之间的关键区别。 具体来说, Buffer.alloc(size, fill) 永远不会使用内部的 Buffer 池,而 Buffer.allocUnsafe(size).fill(fill) 在 size 小于或等于 Buffer.poolSize 的一半时将会使用内部的 Buffer池。 该差异虽然很微妙,但当应用程序需要 Buffer.allocUnsafe() 提供的额外性能时,则非常重要。
buffer 一旦声明,就不能再增加长度。
Buffer.concat(list[, totalLength])
-
list <Buffer[]> | <Uint8Array[]> 要合并的 Buffer 数组或 Uint8Array 数组。
-
totalLength 合并后 list 中的 Buffer 实例的总长度。
返回一个合并了 list 中所有 Buffer 实例的新 Buffer。
如果 list 中没有元素、或 totalLength 为 0,则返回一个长度为 0 的 Buffer。
如果没有提供 totalLength,则通过将 list 中的 Buffer 实例的长度相加来计算得出。
如果提供了 totalLength,则会强制转换为无符号整数。 如果 list 中的 Buffer 合并后的总长度大于 totalLength,则结果会被截断到 totalLength 的长度。
// 用含有三个 `Buffer` 实例的数组创建一个单一的 `Buffer`。
const buf1 = Buffer.alloc(10);
const buf2 = Buffer.alloc(14);
const buf3 = Buffer.alloc(18);
const totalLength = buf1.length + buf2.length + buf3.length;
console.log(totalLength);
// 打印: 42
const bufA = Buffer.concat([buf1, buf2, buf3], totalLength);
console.log(bufA);
// 打印: <Buffer 00 00 00 00 ...>
console.log(bufA.length);
// 打印: 42
Buffer.isBuffer(obj)
如果 obj 是一个 Buffer,则返回 true,否则返回 false。
Buffer.isEncoding(encoding)
encoding 要检查的字符编码名称。
如果 encoding 是支持的字符编码的名称,则返回 true,否则返回 false。
console.log(Buffer.isEncoding('utf-8'));
// 打印: true
console.log(Buffer.isEncoding('hex'));
// 打印: true
console.log(Buffer.isEncoding('utf/8'));
// 打印: false
console.log(Buffer.isEncoding(''));
// 打印: false
buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
-
target | 要拷贝进的 Buffer 或 Uint8Array。
-
targetStart target 中开始写入之前要跳过的字节数。默认值: 0。
-
sourceStart buf 中开始拷贝的偏移量。默认值: 0。
-
sourceEnd buf 中结束拷贝的偏移量(不包含)。默认值: buf.length。
-
返回: 拷贝的字节数。
-
拷贝 buf 中某个区域的数据到 target 中的某个区域,即使 target 的内存区域与 buf 的重叠。
TypedArray#set() 执行相同的操作,并且可用于所有的 TypedArray,包括 Node.js 的 Buffer,尽管它采用不同的函数参数。
// 创建两个 `Buffer` 实例。
const buf1 = Buffer.allocUnsafe(26);
const buf2 = Buffer.allocUnsafe(26).fill('!');
for (let i = 0; i < 26; i++) {
// 97 是 'a' 的十进制 ASCII 值。
buf1[i] = i + 97;
}
// 拷贝 `buf1` 中第 16 至 19 字节偏移量的数据到 `buf2` 第 8 字节偏移量开始。
buf1.copy(buf2, 8, 16, 20);
// 这等效于:
// buf2.set(buf1.subarray(16, 20), 8);
console.log(buf2.toString('ascii', 0, 25));
// 打印: !!!!!!!!qrst!!!!!!!!!!!!!
// 创建一个 `Buffer`,并拷贝同一 `Buffer` 中一个区域的数据到另一个重叠的区域。
const buf = Buffer.allocUnsafe(26);
for (let i = 0; i < 26; i++) {
// 97 是 'a' 的十进制 ASCII 值。
buf[i] = i + 97;
}
buf.copy(buf, 0, 4, 10);
console.log(buf.toString());
// 打印: efghijghijklmnopqrstuvwxyz
buf.equals(otherBuffer)
const buf1 = Buffer.from('ABC');
const buf2 = Buffer.from('414243', 'hex');
const buf3 = Buffer.from('ABCD');
console.log(buf1.equals(buf2));
// 打印: true
console.log(buf1.equals(buf3));
// 打印: false
buf.fill(value[, offset[, end]][, encoding])
-
value | | | 用来填充 buf 的值。
-
offset 开始填充 buf 的偏移量。默认值: 0。
-
end 结束填充 buf 的偏移量(不包含)。默认值: buf.length。
-
encoding 如果 value 是字符串,则指定 value 的字符编码。默认值: 'utf8'。
-
返回: buf 的引用。
-
用指定的 value 填充 buf。 如果没有指定 offset 与 end,则填充整个 buf:
-
fs 模块读取到的内容即为buffer
// 用 ASCII 字符 'h' 填充 `Buffer`。
const b = Buffer.allocUnsafe(50).fill('h');
console.log(b.toString());
// 打印: hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
如果 value 不是字符串、 Buffer、或整数,则会被转换为 uint32 值。 如果得到的整数大于 255(十进制),则 buf 将会使用 value & 255 填充。
如果 fill() 最后写入的是一个多字节字符,则只写入适合 buf 的字节:
// 使用在 UTF-8 中占用两个字节的字符来填充 `Buffer`。
console.log(Buffer.allocUnsafe(5).fill('\u0222'));
// 打印: <Buffer c8 a2 c8 a2 c8>
如果 value 包含无效的字符,则截掉无效的字符。 如果截掉后没有数据,则不填充:
const buf = Buffer.allocUnsafe(5);
console.log(buf.fill('a'));
// 打印: <Buffer 61 61 61 61 61>
console.log(buf.fill('aazz', 'hex'));
// 打印: <Buffer aa aa aa aa aa>
console.log(buf.fill('zz', 'hex'));
// 抛出异常。
- 流文件读取
const fs = require('fs');
const path = require('path');
const rs = fs.createReadSteam(path.resolve(__dirname, 'name.txt'), {
flags: 'r', // r w 读和写权限
highWaterMark: 4, // 每次预计读取多少个
encoding: null,
autoClose: true, // 读取完毕,关闭文件
start: 0,
end: 5 // slice(start, end) 包含end的
})
// 流 默认流是暂停模式,非流动模式,内部会监控你有没有监听data事件 rs.emit('data', 123);
const arr = [];
rs.on('data', function(chunk) {
arr.push(chunk);
rs.pause(); // 暂停data事件的触发 rs.play(); 继续
});
rs.on('end', function() {
console.log(Buffer.concat(arr).toString()); // 读取完毕
});
rs.on('error', function(error) {
console.log(error);
})
文件压缩zlib
基于zlib库和buffer进行文件压缩
const zlib = require('zlib');
const fs = require('fs');
const rs = fs.cerateReadStream('jquery.js');
const ws = fs.cerateWriteStream('jquery.js.gz');
const gz = zlib,createGzip();
rs.pipe(gz).pipe(ws);
ws.on('error', (err) => {
console.log('失败');
})
ws.on('finish', () => {
console.log('完成')
})
服务器压缩文件
const http = require('http'); // 服务模块
const fs = require('fs'); // 读取文件
const zlib = require('zlib'); // 压缩模块
const server = http.cerateServer((req, res) => {
const rs = fs.cerateReadStream(`www${req.url}`);
res.setHeader('content-encoding', 'gzip'); // 设置响应头
const gz = zlib.createGzip(); // gz压缩
rs.pipe(gz).pipe(res); // 压缩后返回给前端
rs.on('error', err => { // 监听失败
res.setHeader(404);
res.write('Not Found');
res.end();
console.log('读取失败')
})
rs.on('finish', err => { // 传输完成
console.log('写入完成')
})
}).listen(8080);
[参考文献-] (nodejs.org/dist/latest…)