返回顶部
首页 > 资讯 > 前端开发 > node.js >浅谈Node.js:Buffer模块
  • 525
分享到

浅谈Node.js:Buffer模块

浅谈模块js 2022-06-04 17:06:19 525人浏览 独家记忆
摘要

javascript在客户端对于unicode编码的数据操作支持非常友好,但是对二进制数据的处理就不尽人意。node.js为了能够处理二进制数据或非unicode编码的数据,便设计了Buffer类,该类实现

javascript在客户端对于unicode编码的数据操作支持非常友好,但是对二进制数据的处理就不尽人意。node.js为了能够处理二进制数据或非unicode编码的数据,便设计了Buffer类,该类实现了Uint8Array接口,并对其进行了优化,它的实例类似于整型数组,但是它的大小在创建后便不可调整。在介绍Buffer如何使用之前,先介绍几个知识点。

1、V8引擎的内存使用限制

V8引擎最大堆内存使用在32位系统上默认为512M,在64位系统上是1GB,虽然可以使用--max-old-space-size参数调整该值,但还是建议要用到大内存的时候使用Buffer或Stream,因为Buffer的内存分配不在V8的堆上。

2、单个Buffer实例大小限制

单个Buffer实例的大小最大数值为1GB-1(32位系统)或2GB-1(64位系统),所以在创建Buffer实例的时候不能超过该值,或者使用readFile()方法读取大文件,否则将抛出RangeError错误。

3、8KB池

nodejs在创建Buffer实例的时候,当用户申请的空间大于8KB,会直接调用内部的createUnsafeBuffer()方法创建一个Buffer,如果申请的空间大于0且小于4KB,新的Buffer则会建立在当前的8kb SLAB上,并更新剩余空间,如下图所示:

查看图片

下面介绍Buffer api的简单使用:

1、创建Buffer实例

使用Buffer.from(), Buffer.alloc(), Buffer.allocUnsafe()等方法来创建一个Buffer实例,6.0版本以前直接使用构造函数创建的方法new Buffer()已被丢弃,不推荐使用,因为有可能会造成内存泄漏。
方法Buffer.alloc(size[, fill[, encoding]]),参数含义如下:

size,指定buffer的长度,但不能超过buffer.kMaxLength,若不是数字则报错 fill,指定初始化buffer的值,默认为0 encoding,如果fill是字符串,则该参数指定fill的编码

使用如下所示:


const buf1 = Buffer.alloc(10);
console.log(buf1);//<Buffer 00 00 00 00 00 00 00 00 00 00>
const buf2 = Buffer.alloc(10,'hello');
console.log(buf2);//<Buffer 68 65 6c 6c 6f 68 65 6c 6c 6f>
const buf3 = Buffer.alloc(10,'hello','base64');
console.log(buf3);//<Buffer 85 e9 65 85 e9 65 85 e9 65 85>

方法Buffer.allocUnsafe(size),size参数指定buffer的大小,该方法返回一个没有初始化的buffer,因此可能还保留有敏感的数据,造成信息的泄漏,建议使用buffer.fill(0)函数初始化buffer,该方法与Buffer.alloc(size, fill)是不一样的,有可能使用8KB池。使用如下所示:


const buf4 = Buffer.allocUnsafe(10);
console.log(buf4);//<Buffer 68 fb 4d 00 00 00 00 00 08 00>,可以看出是有数据的
buf4.fill(0);
console.log(buf4);//<Buffer 00 00 00 00 00 00 00 00 00 00>

方法Buffer.allocUnsafeSlow(size),参数含义同上,该方法不会使用Buffer池,容易造成内存的浪费,使用如下所示:


const buf5 = Buffer.allocUnsafeSlow(10);
console.log(buf5);//<Buffer 38 00 24 00 00 00 00 00 00 00>

方法Buffer.from(value,[...]),这里分为四种情况,如下所示:

第一,value为16进制数组,将数组转化为buffer,如果不是16进制,则会进行转换,如下:


const buf6 = Buffer.from([1,2,3,5,17]);
console.log(buf6);//<Buffer 01 02 03 05 11>

第二,value为字符串,则转换字符串为buffer,该方法会使用buffer池,如下:


const buf7 = Buffer.from('hello world!');
console.log(buf7);//<Buffer 01 02 03 05 11>

第三,value为buffer实例,则将value拷贝至新的buffer中,这里只是值的拷贝,不会共享内存,如下:


const buf8 = Buffer.from('hello world');
const buf9 = Buffer.from(buf8);
console.log(buf8);//<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
console.log(buf9);//<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
buf9[0] = 0x66;
console.log(buf8);//<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
console.log(buf9);//<Buffer 66 65 6c 6c 6f 20 77 6f 72 6c 64>

第四,value为arrayBuffer时,还有两个可选参数[, byteOffset[, length]],byteOffset指定从arrayBuffer开始复制的位置,length复制的长度。如下:


const arr = new Uint8Array(2);
arr[0] = 128;
arr[1] = 200;
const buf10 = Buffer.from(arr,0,2);
console.log(buf10);//<Buffer 80 c8>

如果引用的是arr.buffer,则新创建的buffer buf10与arr共享内存,如下:


const arr = new Uint8Array(2);
arr[0] = 128;
arr[1] = 200;
const buf10 = Buffer.from(arr.buffer);
arr[0] = 254;
console.log(buf10);//<Buffer fe c8>

2、buffer解码

使用buf.toString([encoding[, start[, end]]])方法将buffer转换成字符串,encoding指定字符编码,默认为'utf8',start开始位置,end结束位置(不包括),目前encoding只支持'ascii,utf8,utf16le,ucs2,base64,latin1,binary,hex',使用如下所示:


const buf12 = Buffer.from('我爱中国');
console.log(buf12.toString('base64'));//5oiR54ix5Lit5Zu9
console.log(buf12.toString('utf8'));//我爱中国
console.log(buf12.toString('hex'));//e68891e788b1e4b8ade59bbd

3、buffer拼接、复制、填充、分割

方法buf.fill(value[, offset[, end]][, encoding])使用指定的值填充buffer,参数offset指定填充的起始位置,end为结束位置,使用如下所示:


console.log(Buffer.allocUnsafe(5).fill('a').toString());//aaaaa
console.log(Buffer.allocUnsafe(5).fill(65).toString('utf8'));//AAAAA

方法Buffer.concat(list[, totalLength])将多个buffer合并在一起,并返回一个新的buffer实例,参数totalLength为指定的buffers的长度总和,如果不提供该值,函数内部会循环去获取每一个buffer的长度,然后进行拼接,因此为了速度,最好指定一个总长度,使用如下:


function bufferInjoin(buffArr){
  var len = 0;
  buffArr.forEach((buff,idx,arr)=>{
    len+=buff.length;
  });
  var buffer = Buffer.concat(buffArr,len);
  return buffer;
}
var buff = bufferInjoin([Buffer.from('hehe'),Buffer.allocUnsafe(5).fill('a')]);
console.log(buff);//<Buffer 68 65 68 65 61 61 61 61 61>
console.log(buff.length);//9
console.log(buff.toString());//heheaaaaa

方法buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])可以实现buf到target的复制,参数含义如下:

target,复制目标 targetStart,复制目标开始被覆盖的位置 sourceStart,复制源开始复制的位置 sourceEnd,复制源复制结束的位置

使用如下所示:


const buf1 = Buffer.from('hello world!');
const buf2 = Buffer.allocUnsafe(5).fill('x');
buf1.copy(buf2,0,0,5);
console.log(buf2.toString());//hello

方法buf.slice([start[, end]])可以分割buffer,返回一个新的buffer,但是仍然是引用原buffer,因此改变原buffer数据,该新buffer也会跟着改变,如果参数start,end为负数,则先要加上buffer的长度再进行计算,如下所示:


const buf1 = Buffer.from('hello world.');
const buf2 = buf1.slice(0);
console.log(buf2);//<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64 2e>
buf2[0] = 88;
console.log(buf1);//<Buffer 58 65 6c 6c 6f 20 77 6f 72 6c 64 2e>
const buf3 = buf1.slice(-6,-1);
console.log(buf3.toString());//world

3、buffer读写

buffer写操作通过write开头的写api来完成,主要有以下这些:

buf.write(string[, offset[, length]][, encoding]),向buffer写入字符串 buf.writeDoubleBE(value, offset[, noAssert])写入64位浮点型数字,大端对齐 buf.writeDoubleLE(value, offset[, noAssert]),写入64位浮点型数字,小端对齐 buf.writeFloatBE(value, offset[, noAssert]),写入32位浮点型数字,大端对齐 buf.writeFloatLE(value, offset[, noAssert]),写入32位浮点型数字,小端对齐 buf.writeInt8(value, offset[, noAssert]),写入有符号8位整型数字 buf.writeInt16BE(value, offset[, noAssert]),写入有符号16位整型数字,大端对齐 buf.writeInt16LE(value, offset[, noAssert]),写入有符号16位整型数字,小端对齐 buf.writeInt32BE(value, offset[, noAssert]),写入有符号32位整型数字,大端对齐 buf.writeInt32LE(value, offset[, noAssert]),写入有符号32位整型数字,小端对齐 buf.writeIntBE(value, offset, byteLength[, noAssert]),以下便不再累述 buf.writeIntLE(value, offset, byteLength[, noAssert]) buf.writeUInt8(value, offset[, noAssert]) buf.writeUInt16BE(value, offset[, noAssert]) buf.writeUInt16LE(value, offset[, noAssert]) buf.writeUInt32BE(value, offset[, noAssert]) buf.writeUInt32LE(value, offset[, noAssert]) buf.writeUIntBE(value, offset, byteLength[, noAssert]) buf.writeUIntLE(value, offset, byteLength[, noAssert])

buffer读操作由read开头的api完成,主要有以下这些:

buf.readDoubleBE(offset[, noAssert]) buf.readDoubleLE(offset[, noAssert]) buf.readFloatBE(offset[, noAssert]) buf.readFloatLE(offset[, noAssert]) buf.readInt8(offset[, noAssert]) buf.readInt16BE(offset[, noAssert]) buf.readInt16LE(offset[, noAssert]) buf.readInt32BE(offset[, noAssert]) buf.readInt32LE(offset[, noAssert]) buf.readIntBE(offset, byteLength[, noAssert]) buf.readIntLE(offset, byteLength[, noAssert]) buf.readUInt8(offset[, noAssert]) buf.readUInt16BE(offset[, noAssert]) buf.readUInt16LE(offset[, noAssert]) buf.readUInt32BE(offset[, noAssert]) buf.readUInt32LE(offset[, noAssert]) buf.readUIntBE(offset, byteLength[, noAssert]) buf.readUIntLE(offset, byteLength[, noAssert])

使用如下所示,以32无符号整型为例:


const buf = Buffer.allocUnsafe(8);
buf.writeUInt32BE(0x12345678,0)
console.log(buf);
const data = buf.readUInt32BE(0);
console.log(data.toString(16));

最后利用buffer读API完成一个获取PNG格式图片尺寸的小工具,在开始编码之前,先简单介绍下PNG文件组成,如下所示:

PNG文件标志 PNG数据块 …… PNG数据块

这里我们只要用到PNG文件标识和PNG数据块的第一个块IHDR文件头数据块。文件标识是固定的8个字节,为89 50 4E 47 0D 0A 1A 0A,IHDR数据块的长度为13个字节,格式如下:

域的名称 字节数 说明 Width 4 bytes 宽度 Height 4 bytes 高度 Bit depth 1 bytes 图像深度 ColorType 1 bytes 颜色类型 Compression method 1 bytes 压缩方法 Filter method 1 bytes 滤波器方法 Interlace method 1 bytes 隔行扫描方法

开始编码,如下所示:


const fs = require('fs');
const path = require('path');

const argvs = process.argv.slice(2);
if(argvs.length<=0){
  console.error('请输入图片:png.js img1 img2 ...');
  process.exit(-1);
}
argvs.forEach((img,idx,arr)=>{
  var stat = fs.statSync(img);
  fs.open(img,'r',(err,fd)=>{
    if(err) throw err;
    var buff = Buffer.alloc(stat.size);
    fs.read(fd,buff,0,stat.size,0,(err, bytesRead, buffer)=>{
      if(err) throw err;
      fs.close(fd,()=>{});
      getImgDimension(buff,(err,dimension)=>{
        if(err) throw err;
        console.log(`${img}的尺寸为:${dimension.width}x${dimension.height}`);
      });
    });
  });
});
function getImgDimension(buff,cb){
  if((buff.toString('utf8',1,8) === 'PNGrnx1an') && (buff.toString('utf8',12,16) === 'IHDR')){
    return cb(null,{
      width:buff.readUInt32BE(16),
      height:buff.readUInt32BE(20)
    }),!0;
  }else{
    return cb(new Error('不是PNG图片'),{}),!1;
  }
}

执行结果如下:

E:developmentdocumentnodejsdemo>node png.js 20160824083157.png 下载.png
20160824083157.png的尺寸为:195x195
下载.png的尺寸为:720x600

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: 浅谈Node.js:Buffer模块

本文链接: https://lsjlt.com/news/12875.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 浅谈Node.js:Buffer模块
    Javascript在客户端对于unicode编码的数据操作支持非常友好,但是对二进制数据的处理就不尽人意。Node.js为了能够处理二进制数据或非unicode编码的数据,便设计了Buffer类,该类实现...
    99+
    2022-06-04
    浅谈 模块 js
  • 浅谈Node.js:fs文件系统模块
    fs文件系统模块,这是一个非常重要的模块,对文件的操作都基于它。该模块的所有方法都有同步和异步两种方式,下面便介绍一下该模块的使用。 1、检测当前进程对文件的权限 使用fs.access(path[, mo...
    99+
    2022-06-04
    浅谈 文件系统 模块
  • 浅谈Python模块导入规范
    模块导入的规范 模块是类或函数的集合,用于实现某个功能。模块的导入和Java 中包的导入的概念很相似都使用import语句。在Python中,如果需要在程序中调用标准库或其他第三方...
    99+
    2024-04-02
  • 浅谈一下python中threading模块
    目录一、Thread的使用二、threading.activeCount()的使用三、threading.enumerate()的使用。四、threading.setDaemon()...
    99+
    2023-05-18
    python threading threading模块
  • 浅谈Node模块系统及其模式
    模块是构建应用程序的基础,也使得函数和变量私有化,不直接对外暴露出来,接下来我们就要介绍Node的模块化系统和它最常用的模式 为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。...
    99+
    2022-06-04
    浅谈 模块 模式
  • 浅谈python中常用的excel模块库
    目录openpyxl如何安装:使用效果之⼀:xlwings如何安装:使用效果之⼀:XlsxWriter如何安装:使用效果之⼀:Tablibxlrd如何安装:xlwtxluti...
    99+
    2022-06-02
    python excel模块
  • 浅谈python jieba分词模块的基本用法
    jieba(结巴)是一个强大的分词库,完美支持中文分词,本文对其基本用法做一个简要总结。 特点 支持三种分词模式: 精确模式,试图将句子最精确地切开,适合文本分析; 全模式,把句...
    99+
    2022-06-04
    分词 浅谈 模块
  • 浅谈JS前端模块化的几种规范
    目录前言前端模块化开发的价值恼人的命名冲突繁琐的文件依赖模块化的好处CommonJS 规范CommonJS 与 ES6 模块的差异AMD 规范CMD 规范UMD 规范回到正题总结前言...
    99+
    2024-04-02
  • configParser模块详谈
      使用配置文件来灵活的配置一些参数是一件很常见的事情,配置文件的解析并不复杂,在python里更是如此,在官方发布的库中就包含有做这件事情的库,那就是configParser   configParser解析的配置文件的格式比较象i...
    99+
    2023-01-31
    模块 configParser
  • 浅谈Python安装并使用redis模块的方法
    这篇文章给大家分享的是有关浅谈Python安装并使用redis模块的方法的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。安装和使用安装安装redis模块pip3 inst...
    99+
    2024-04-02
  • 浅谈Spring注入模型
    目录Spring注入bean的方式Spring的注入模型代码示例定义bean对象定义配置类定义后置处理器定义测试方法自动注入和手动注入Spring注入bean的方式 DI exist...
    99+
    2023-05-18
    Spring注入模型 Spring注入
  • python simplejson模块浅
    一、背景知识JSON:引用百科描述如下,具体请自行搜索相关介绍:    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于JavaScript(Standard ECMA-262 3rd ...
    99+
    2023-01-31
    模块 python simplejson
  • 浅谈Nodejs观察者模式
    一、前言 Nodejs使用有些日子了,近来再回顾下其API、多使用新特性,以期有更高层次的掌握,本次API的总结区别于单纯对英文版的汉化,会多做些扩展和自己的理解,希望对大家有所帮助,先从最核心的Event...
    99+
    2022-06-04
    观察者 浅谈 模式
  • 浅谈Python责任链模式
    目录介绍实现方式案例测试使用场景介绍 责任链模式是一种行为型设计模式,它允许多个对象以链式的形式依次处理请求,直到请求被处理或者无处理对象为止 实现方式 责任链模式由多个处理器组成,...
    99+
    2023-05-16
    Python 责任链模式
  • 浅谈Java解释器模式
    ​ **请注意!请注意!!!**今天讲给大家讲解非常“有用”的设计模式,解释器模式!!! ​ 设计模式有三大种类,一种是创建型模式,一种是结构型模式,最后一种...
    99+
    2024-04-02
  • 简单谈谈Python中的模块导入
    目录模块与包 __import__ 模块缓存 imp 与 importlib 模块 惰性导入 总结参考资料本文不讨论 Python 的导入机制(底层实现细节),仅讨论模块与包,以及导...
    99+
    2024-04-02
  • 浅谈IDEA实用的Servlet模板
    目录一、前言二、这是模板内容,直接创建自己的模板复制用即可三、优点四、问题一、前言 不会再IDEA中创建模板点击这里看教程 二、这是模板内容,直接创建自己的模板复制用即可 #if...
    99+
    2024-04-02
  • 浅谈 C++17 里的 Visitor 模式
    目录一、Visitor Pattern1、组成2、接口3、场景4、特点5、实现二、Epilogue一、Visitor Pattern 访问者模式是一种行为模式,允许任意的分离的访问者...
    99+
    2024-04-02
  • 浅谈VUE uni-app 模板语法
    1.v-bind(简写 :) 组件属性中要使用data中定义的数据变量,或组件属性要使用表达式,需用v-bind指定 简写 : 2.v-on(简写@) 监听DOM事件 cli...
    99+
    2024-04-02
  • 浅析Node的events模块
    在 vue 的项目中,有时我们会用到全局事件总线来管理组件之间的通信。vue2 项目里我们可以自己借助 $emit、 $on 和 $off 封装一个 eventHub;vue3 中 $on 和 $off 被移除了,我们可以使用 mitt 库...
    99+
    2023-05-14
    nodejs node
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作