将二进制NodeJS缓冲区转换为JavaScript ArrayBuffer

Dra*_*ara 116 javascript binary buffer node.js arraybuffer

如何将NodeJS二进制缓冲区转换为JavaScript ArrayBuffer?

Mar*_*son 116

实例Buffer也是Uint8Array node.js 4.x及更高版本的实例.因此,最有效的解决方案是buf.buffer直接访问该属性,根据/sf/answers/2197598021/.如果你需要转向另一个方向,Buffer构造函数也会获取一个ArrayBufferView参数.

请注意,这不会创建副本,这意味着对任何ArrayBufferView的写入将写入原始Buffer实例.


在旧版本中,node.js将ArrayBuffer作为v8的一部分,但Buffer类提供了更灵活的API.为了读取或写入ArrayBuffer,您只需要创建一个视图并进行复制.

从Buffer到ArrayBuffer:

function toArrayBuffer(buf) {
    var ab = new ArrayBuffer(buf.length);
    var view = new Uint8Array(ab);
    for (var i = 0; i < buf.length; ++i) {
        view[i] = buf[i];
    }
    return ab;
}
Run Code Online (Sandbox Code Playgroud)

从ArrayBuffer到Buffer:

function toBuffer(ab) {
    var buf = Buffer.alloc(ab.byteLength);
    var view = new Uint8Array(ab);
    for (var i = 0; i < buf.length; ++i) {
        buf[i] = view[i];
    }
    return buf;
}
Run Code Online (Sandbox Code Playgroud)

  • 我还建议您通过使用DataView尽可能复制整数来优化它.直到`size&0xfffffffe`,复制32位整数,然后,如果剩下1个字节,则复制8位整数,如果是2个字节,则复制16位整数,如果复制3个字节,则复制16位和8位整数. (5认同)
  • 请参阅kraag22的答案,以便更简单地实现其中的一半. (3认同)
  • 为什么返回“ ab”?`ab`有什么用?结果总是得到“ {}”。 (2认同)
  • **'`slice()` 方法返回一个新的 `ArrayBuffer`,其内容是这个 `ArrayBuffer` 字节的副本,从开始、包含、到结束、独占。'** - [MDN `ArrayBuffer.prototype .slice()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/slice) (2认同)

Zac*_*chB 54

  • 没有依赖关系,最快,节点4.x和更高版本

缓冲区是Uint8Arrays,所以你只需要访问它的ArrayBuffer.这是O(1):

 // node buffer
var b = new Buffer(512);
 // ArrayBuffer
var ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
 // TypedArray
var ui32 = new Uint32Array(b.buffer, b.byteOffset, b.byteLength / Uint32Array.BYTES_PER_ELEMENT);
Run Code Online (Sandbox Code Playgroud)

slice和偏移的东西是需要,因为小缓冲器(<4096个字节,我认为)正使用一个共享ArrayBuffer视图.如果没有它,您最终可能会得到一个包含来自另一个TypedArray的数据的ArrayBuffer.

  • 无依赖关系,速度适中,任何版本的节点

使用Martin Thomson的答案,该答案O(n)时间内运行.(另请参阅我对他关于非优化的答案的评论的回复.使用DataView很慢.即使你需要翻转字节,也有更快的方法.)

  • 依赖性,快速,任何版本的节点

您可以使用https://www.npmjs.com/package/memcpy向任一方向(Buffer to ArrayBuffer and back).它比这里发布的其他答案更快,并且是一个写得很好的库.节点0.12到iojs 3.x需要ngossen的fork(见这个).


kra*_*g22 49

"从ArrayBuffer到Buffer"可以这样做:

var buffer = Buffer.from( new Uint8Array(ab) );
Run Code Online (Sandbox Code Playgroud)

  • 但这就是我想用谷歌搜索我的问题,很高兴我找到了解决方案. (31认同)
  • 这与OP想要的相反. (23认同)

Dav*_*oks 25

写一个更快的方法

var arrayBuffer = new Uint8Array(nodeBuffer).buffer;
Run Code Online (Sandbox Code Playgroud)

但是,对于1024个元素的缓冲区,这似乎比建议的toArrayBuffer函数慢大约4倍.

  • 后期补充:@trevnorris [说](https://github.com/joyent/node/issues/7609#issuecomment-108562986)"从[V8]开始4.3缓冲区由Uint8Array支持",所以现在可能会更快. .. (3认同)
  • 用v5.6.0测试它是最快的 (3认同)

Fer*_*oss 12

使用以下优秀的npm包:to-arraybuffer.

或者,您可以自己实现它.如果调用了缓冲区buf,请执行以下操作:

buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)
Run Code Online (Sandbox Code Playgroud)

  • [Joyent把它拿出来](https://github.com/joyent/node/issues/7609#issuecomment-42903457) (21认同)

Кон*_*Ван 9

1.一种Buffer仅仅是一个视图用于寻找到的ArrayBuffer.

Buffer,其实是一个FastBuffer,它extends(继承)Uint8Array,这是一个八位位组单元视图的实际存储器,的("局部存取") ArrayBuffer.

  /lib/buffer.js#L65-L73 Node.js 9.4.0
class FastBuffer extends Uint8Array {
  constructor(arg1, arg2, arg3) {
    super(arg1, arg2, arg3);
  }
}
FastBuffer.prototype.constructor = Buffer;
internalBuffer.FastBuffer = FastBuffer;

Buffer.prototype = FastBuffer.prototype;
Run Code Online (Sandbox Code Playgroud)

2. 视图ArrayBuffer的大小和视图的大小可能会有所不同.

理由#1 : Buffer.from(arrayBuffer[, byteOffset[, length]]).

使用Buffer.from(arrayBuffer[, byteOffset[, length]]),您可以创建一个Buffer指定其底层ArrayBuffer和视图的位置和大小.

const test_buffer = Buffer.from(new ArrayBuffer(50), 40, 10);
console.info(test_buffer.buffer.byteLength); // 50; the size of the memory.
console.info(test_buffer.length); // 10; the size of the view.
Run Code Online (Sandbox Code Playgroud)

理由2:FastBuffer内存分配.

它根据大小以两种不同的方式分配内存.

  • 如果大小小于内存池大小的一半而不是0("小"):它使用内存池来准备所需的内存.
  • 否则:它会创建一个ArrayBuffer完全符合所需内存的专用.
  /lib/buffer.js#L306-L320 Node.js 9.4.0
function allocate(size) {
  if (size <= 0) {
    return new FastBuffer();
  }
  if (size < (Buffer.poolSize >>> 1)) {
    if (size > (poolSize - poolOffset))
      createPool();
    var b = new FastBuffer(allocPool, poolOffset, size);
    poolOffset += size;
    alignPool();
    return b;
  } else {
    return createUnsafeBuffer(size);
  }
}
Run Code Online (Sandbox Code Playgroud)   /lib/buffer.js#L98-L100 Node.js 9.4.0
function createUnsafeBuffer(size) {
  return new FastBuffer(createUnsafeArrayBuffer(size));
}
Run Code Online (Sandbox Code Playgroud)

" 内存池 " 是什么意思?

存储器池是一个固定大小的预先分配用于保持小尺寸存储块用于存储块Buffer秒.使用它可以将小型内存块紧密地保持在一起,从而防止由小型内存块的单独管理(分配和释放)引起的碎片.

在这种情况下,内存池是ArrayBuffers,默认大小为8 KiB,在中指定Buffer.poolSize.当它为a提供一个小型内存块时Buffer,它会检查最后一个内存池是否有足够的可用内存来处理这个问题.如果是的话,它会创建一个Buffer"意见"的内存池的给定的部分块,否则,将创建一个新的内存池等.


您可以访问底层ArrayBufferBuffer.该Bufferbuffer财产(即从继承Uint8Array)持有它.一个"小" Bufferbuffer属性是ArrayBuffer代表整个内存池.因此,在这种情况下,ArrayBufferBuffer大小各不相同.

const zero_sized_buffer = Buffer.allocUnsafe(0);
const small_buffer = Buffer.from([0xC0, 0xFF, 0xEE]);
const big_buffer = Buffer.allocUnsafe(Buffer.poolSize >>> 1);

// A `Buffer`'s `length` property holds the size, in octets, of the view.
// An `ArrayBuffer`'s `byteLength` property holds the size, in octets, of its data.

console.info(zero_sized_buffer.length); /// 0; the view's size.
console.info(zero_sized_buffer.buffer.byteLength); /// 0; the memory..'s size.
console.info(Buffer.poolSize); /// 8192; a memory pool's size.

console.info(small_buffer.length); /// 3; the view's size.
console.info(small_buffer.buffer.byteLength); /// 8192; the memory pool's size.
console.info(Buffer.poolSize); /// 8192; a memory pool's size.

console.info(big_buffer.length); /// 4096; the view's size.
console.info(big_buffer.buffer.byteLength); /// 4096; the memory's size.
console.info(Buffer.poolSize); /// 8192; a memory pool's size.
Run Code Online (Sandbox Code Playgroud)

所以我们需要提取它" 观看 " 的记忆.

An的ArrayBuffer大小是固定的,因此我们需要通过复制零件来提取它.为此,我们使用继承自BufferbyteOffset属性length属性Uint8Array,以及制作一部分副本ArrayBuffer.prototype.slice方法ArrayBuffer.这里的slice()-ing方法灵感来自@ZachB.

const test_buffer = Buffer.from(new ArrayBuffer(10));
const zero_sized_buffer = Buffer.allocUnsafe(0);
const small_buffer = Buffer.from([0xC0, 0xFF, 0xEE]);
const big_buffer = Buffer.allocUnsafe(Buffer.poolSize >>> 1);

function extract_arraybuffer(buf)
{
    // You may use the `byteLength` property instead of the `length` one.
    return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
}

// A copy -
const test_arraybuffer = extract_arraybuffer(test_buffer); // of the memory.
const zero_sized_arraybuffer = extract_arraybuffer(zero_sized_buffer); // of the... void.
const small_arraybuffer = extract_arraybuffer(small_buffer); // of the part of the memory.
const big_arraybuffer = extract_arraybuffer(big_buffer); // of the memory.

console.info(test_arraybuffer.byteLength); // 10
console.info(zero_sized_arraybuffer.byteLength); // 0
console.info(small_arraybuffer.byteLength); // 3
console.info(big_arraybuffer.byteLength); // 4096
Run Code Online (Sandbox Code Playgroud)

4.绩效改进

如果您要将结果用作只读,或者可以修改输入Buffer的内容,则可以避免不必要的内存复制.

const test_buffer = Buffer.from(new ArrayBuffer(10));
const zero_sized_buffer = Buffer.allocUnsafe(0);
const small_buffer = Buffer.from([0xC0, 0xFF, 0xEE]);
const big_buffer = Buffer.allocUnsafe(Buffer.poolSize >>> 1);

function obtain_arraybuffer(buf)
{
    if(buf.length === buf.buffer.byteLength)
    {
        return buf.buffer;
    } // else:
    // You may use the `byteLength` property instead of the `length` one.
    return buf.subarray(0, buf.length);
}

// Its underlying `ArrayBuffer`.
const test_arraybuffer = obtain_arraybuffer(test_buffer);
// Just a zero-sized `ArrayBuffer`.
const zero_sized_arraybuffer = obtain_arraybuffer(zero_sized_buffer);
// A copy of the part of the memory.
const small_arraybuffer = obtain_arraybuffer(small_buffer);
// Its underlying `ArrayBuffer`.
const big_arraybuffer = obtain_arraybuffer(big_buffer);

console.info(test_arraybuffer.byteLength); // 10
console.info(zero_sized_arraybuffer.byteLength); // 0
console.info(small_arraybuffer.byteLength); // 3
console.info(big_arraybuffer.byteLength); // 4096
Run Code Online (Sandbox Code Playgroud)

  • 这一切都很好......但你真的回答了OP的问题吗?如果你这样做了,那就埋葬了...... (4认同)

Ste*_*gin 5

ABuffer是 aviewArrayBufferArrayBuffer您可以使用该属性获取内部包装buffer

这是共享内存,不需要复制。

const arrayBuffer = theBuffer.buffer
Run Code Online (Sandbox Code Playgroud)

如果您想要其中copy的数据,请Buffer从原始数据Buffer(而不是从包装的 ArrayBuffer)创建另一个数据,然后引用其包装的ArrayBuffer.

const newArrayBuffer = Buffer.from(theBuffer).buffer
Run Code Online (Sandbox Code Playgroud)

作为参考,从另一个方向,从 anArrayBuffer到 aBuffer

const arrayBuffer = getArrayBuffer()
const sharedBuffer = Buffer.from(arrayBuffer)

const copiedBuffer = Buffer.from(sharedBuffer)
const copiedArrayBuffer = copiedBuffer.buffer
Run Code Online (Sandbox Code Playgroud)


aru*_*ngh -2

NodeJS 在某一时刻(我认为是 v0.6.x)支持 ArrayBuffer。我在这里创建了一个用于 Base64 编码和解码的小型库,但自从更新到 v0.7 后,测试(在 NodeJS 上)失败了。我正在考虑创建一些东西来规范化这一点,但在那之前,我认为Buffer应该使用 Node 的本机。