이진 NodeJS 버퍼를 JavaScript ArrayBuffer로 변환


133

NodeJS 바이너리 버퍼를 JavaScript ArrayBuffer로 어떻게 변환 할 수 있습니까?


1
왜 이렇게해야하는지 궁금합니다.
Chris Biscardi

14
좋은 예는 브라우저에서 파일과 함께 작동하는 라이브러리와 NodeJS 파일을 작성하는 것입니다.
fbstj

1
또는 NodeJS에서 브라우저 라이브러리 사용
OrangeDog

1
또 다른 이유는 플로트가에 저장 될 때 너무 많은 바이트의 RAM을 사용하기 때문 Array입니다. 따라서 많은 수레를 저장하려면 Float32Array4 바이트가 필요한 곳 이 필요 합니다. 그리고 부동 소수점을 파일로 빠르게 Buffer직렬화하려면 JSON으로 직렬화하는 데 시간이 오래 걸리기 때문에 가 필요합니다 .
nponeccop

나는 한 WebRTC를 사용하여 일반 데이터를 보낼 정확히 같은 일을 알고 싶어하며 많은 해답이 여기 ... 너무 많은 좋아하는이 있지만 실제 질문에 대답하지 않는 것이 믿을 수
펠릭스 Crazzolara

답변:


134

의 인스턴스 BufferUint8Array node.js 4.x 이상의 인스턴스입니다 . 따라서 가장 효율적인 솔루션은 https://stackoverflow.com/a/31394257/1375574에 따라 buf.buffer속성에 직접 액세스하는 것 입니다. Buffer 생성자는 다른 방향으로 이동해야하는 경우 ArrayBufferView 인수도 사용합니다.

이렇게하면 복사본이 만들어지지 않습니다. 즉, ArrayBufferView에 쓰면 원래 Buffer 인스턴스에 쓰입니다.


이전 버전에서 node.js에는 v8의 일부로 ArrayBuffer가 모두 있지만 Buffer 클래스는보다 유연한 API를 제공합니다. 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;
}

ArrayBuffer에서 버퍼로 :

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;
}

5
또한 가능한 경우 DataView를 사용하여 정수를 복사하여이를 최적화하는 것이 좋습니다. 까지 size&0xfffffffe32 비트 정수를 복사 한 다음, 1 바이트가 남아 있으면 8 비트 정수를 복사하고, 2 바이트이면 16 비트 정수를 복사하고, 3 바이트이면 16 비트 및 8 비트 정수를 복사하십시오.
Triang3l

3
이 중 절반의 간단한 구현에 대해서는 kraag22의 답변을 참조하십시오.
OrangeDog 2016 년

브라우저 사용을위한 모듈로 Buffer-> ArrayBuffer를 테스트했으며 훌륭하게 작동합니다. 감사!
pospi

3
ab반환됩니까? 아무런 작업이 ab없습니까? 나는 항상 {}결과를 얻는다 .
Andi Giga

1
'이 slice()방법은 ArrayBuffer내용이 ArrayBuffer시작부터 포함, 끝까지 의이 바이트 의 사본 인 새로운 것을 반환합니다 .' - MDNArrayBuffer.prototype.slice()
Константин Ван

61

종속성 없음, 가장 빠른 Node.js 4.x 이상

BufferUint8Arrays이므로 백업 영역을 슬라이스 (복사)하면 ArrayBuffer됩니다.

// Original Buffer
let b = Buffer.alloc(512);
// Slice (copy) its segment of the underlying ArrayBuffer
let ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);

slice오프셋 물건이되어 필요한 작은 때문에 Buffer의 (기본적으로 이하 4 KB, 절반 풀 크기가 ) 공유에 전망 할 수있다 ArrayBuffer. 슬라이스하지 않으면 ArrayBuffer다른 데이터를 포함하는 데이터로 끝날 수 있습니다 Buffer. 문서의 설명을 참조하십시오 .

궁극적으로가 필요한 경우 TypedArray데이터를 복사하지 않고 생성 할 수 있습니다.

// Create a new view of the ArrayBuffer without copying
let ui32 = new Uint32Array(b.buffer, b.byteOffset, b.byteLength / Uint32Array.BYTES_PER_ELEMENT);

Node.js의 모든 의존성, 적당한 속도 없음

O (n) 시간에 실행되는 Martin Thomson의 답변을 사용하십시오 . (최적화에 대한 그의 답변에 대한 의견에 대한 내 답변을 참조하십시오. DataView 사용은 느립니다. 바이트를 뒤집어 야하는 경우에도 더 빠른 방법이 있습니다.)

종속성, 빠름, Node.js ≤ 0.12 또는 iojs 3.x

https://www.npmjs.com/package/memcpy 를 사용 하여 어느 방향 으로든 이동할 수 있습니다 (Buffer to ArrayBuffer 및 back). 여기에 게시 된 다른 답변보다 빠르며 잘 작성된 라이브러리입니다. (참조의에게 포크를 ngossen 필요 3.x를 iojs을 통해 노드 0.12 ).


다시 컴파일하지 않습니다 node> 0.12
Pawel Veselov

1
:의 ngossen 포크 사용 github.com/dcodeIO/node-memcpy/pull/6을 . 노드 4 이상을 사용하는 경우 새로운 답변을 참조하십시오.
ZachB

를 Where을했다 .byteLength.byteOffset문서화?
Константин Ван


1
var ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);내 하루를 구했다
Alexey Sh.

56

"ArrayBuffer에서 버퍼로"는 다음과 같이 수행 할 수 있습니다.

var buffer = Buffer.from( new Uint8Array(ab) );

27
그것은 OP가 원했던 것과 반대입니다.
Alexander Gonchiy

43
그러나 그것이 내 문제를 인터넷으로 찾고 싶었고 해결책을 찾았다.
Maciej Krawczyk

27

작성하는 더 빠른 방법

var arrayBuffer = new Uint8Array(nodeBuffer).buffer;

그러나 이는 1024 개의 요소가있는 버퍼에서 제안 된 toArrayBuffer 함수보다 약 4 배 느리게 실행되는 것으로 보입니다.


3
늦은 추가 : @trevnorris "[V8] 4.3에서 시작하는 버퍼는 Uint8Array에 의해 지원됩니다" 라고 말합니다 . 아마도 이것이 더 빠를 것입니다 ...
ChrisV

안전한 방법은 내 대답을 참조하십시오.
ZachB

3
v5.6.0으로 테스트했으며 가장 빠름
daksh_019

1
이는 Node.js 4.x 이상의 Buffer인스턴스도 인스턴스 이기 때문에 작동합니다 Uint8Array. 더 낮은 Node.js 버전의 경우 toArrayBuffer함수 를 구현해야 합니다.
Benny Neugebauer

14

1. A Buffer는 을 살펴 보기 위한 보기 일뿐 ArrayBuffer입니다.

A는 Buffer, 사실,이며 FastBuffer, 이는 extends(로부터 상속)를 Uint8Array옥텟 단위 인 실제 메모리, 중 ( "부분적인 접근은") ArrayBuffer.

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

Buffer.prototype = FastBuffer.prototype;

2.의 크기 ArrayBuffer보기 의 크기 는 다를 수 있습니다.

이유 # 1 : Buffer.from(arrayBuffer[, byteOffset[, length]]).

을 사용하면 기본 및 뷰의 위치와 크기 를 지정하여을 Buffer.from(arrayBuffer[, byteOffset[, length]])만들 수 있습니다 .BufferArrayBuffer

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.

이유 # 2 : FastBuffer의 메모리 할당.

크기에 따라 두 가지 다른 방식으로 메모리를 할당합니다.

  • 크기가 메모리 풀 크기의 절반보다 작고 0이 아닌 경우 ( "작음") : 메모리 풀 을 사용 하여 필요한 메모리를 준비합니다.
  • 그렇지 않으면 : ArrayBuffer필요한 메모리에 정확히 맞는 전용 을 만듭니다 .
  📜 Node.js를 9.4.0/lib/buffer.js#L306-L320
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);
  }
}
  📜 Node.js를 9.4.0/lib/buffer.js#L98-L100
function createUnsafeBuffer(size) {
  return new FastBuffer(createUnsafeArrayBuffer(size));
}

" 메모리 풀 "이란 무엇입니까 ?

메모리 풀을 고정 된 크기이다 사전 할당 을위한 작은 크기의 메모리 청크를 유지하기위한 메모리 블록 Buffer들. 이를 사용하면 작은 크기의 메모리 청크가 단단히 고정되므로 작은 크기의 메모리 청크 를 별도로 관리 (할당 및 할당 해제)하여 발생 하는 조각화를 방지 할 수 있습니다.

이 경우 메모리 풀 ArrayBuffer의 크기는 기본적으로 8KiB이며에 지정되어 Buffer.poolSize있습니다. a에 작은 크기의 메모리 청크를 제공하려는 Buffer경우 마지막 메모리 풀에이를 처리 할 수있는 충분한 메모리가 있는지 확인합니다. 그렇다면 메모리 풀의 주어진 부분 청크 Buffer"보는" 것을 생성하고, 그렇지 않으면 새로운 메모리 풀을 생성합니다.


의 기본 ArrayBuffer에 액세스 할 수 있습니다 Buffer. Bufferbuffer재산 에서 상속 (Uint8Array )를 보유하고 있습니다. "작은"속성은입니다 전체 메모리 풀을 나타냅니다. 따라서이 경우에는 및의 크기 가 다릅니다. BufferbufferArrayBufferArrayBufferBuffer

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.

3. 따라서“ 보기 ”메모리를 추출해야합니다 .

ArrayBuffer크기는 고정되어 있으므로 부품을 복사하여 추출해야합니다. 이를 위해 우리는 BufferbyteOffsetpropertylengthproperty 를 사용합니다.Uint8Array방법 의 일부의 사본을 만든다 . 여기에서 -ing 방법은 @ZachB 에서 영감을 얻었습니다 .ArrayBuffer.prototype.sliceArrayBufferslice()

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

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

4
이것은 모두 훌륭합니다 ...하지만 실제로 OP의 질문에 대답 했습니까? 당신이 그랬다면, 그것은 묻혔다.
Tustin2121

좋은 답변입니다! 에서 obtain_arraybuffer: buf.buffer.subarray존재하지 않는 것 같습니다. buf.buffer.slice여기에 있었 습니까 ?
매일 생산적인

@everydayproductive 감사합니다. 편집 기록에서 볼 수 있듯이 실제로이를 사용 ArrayBuffer.prototype.slice하여 나중에 수정했습니다 Uint8Array.prototype.subarray. 아, 그리고 내가 잘못했다. 그 당시에는 약간 혼란 스러웠을 것입니다. 당신 덕분에 지금은 모두 좋습니다.
Константин Ван


1

당신은 ArrayBuffer유형으로 생각할 수 있습니다 Buffer.

ArrayBuffer그러므로 항상 유형 (소위 "배열 버퍼보기")를 필요로한다. 일반적으로 배열 버퍼보기 의 유형은 Uint8Array또는 Uint16Array입니다.

Renato Mangini 의 ArrayBuffer와 String 간의 변환 에 대한 좋은 기사가 있습니다 .

코드 예제 (Node.js의 경우)에서 필수 부분을 요약했습니다. 또한 typed ArrayBuffer와 untyped 사이를 변환하는 방법을 보여줍니다 Buffer.

function stringToArrayBuffer(string) {
  const arrayBuffer = new ArrayBuffer(string.length);
  const arrayBufferView = new Uint8Array(arrayBuffer);
  for (let i = 0; i < string.length; i++) {
    arrayBufferView[i] = string.charCodeAt(i);
  }
  return arrayBuffer;
}

function arrayBufferToString(buffer) {
  return String.fromCharCode.apply(null, new Uint8Array(buffer));
}

const helloWorld = stringToArrayBuffer('Hello, World!'); // "ArrayBuffer" (Uint8Array)
const encodedString = new Buffer(helloWorld).toString('base64'); // "string"
const decodedBuffer = Buffer.from(encodedString, 'base64'); // "Buffer"
const decodedArrayBuffer = new Uint8Array(decodedBuffer).buffer; // "ArrayBuffer" (Uint8Array)

console.log(arrayBufferToString(decodedArrayBuffer)); // prints "Hello, World!"

0

Float64Array에 대해 위의 시도했지만 작동하지 않았습니다.

나는 실제로 데이터가 올바른 덩어리로 뷰를 'INTO'해야한다는 것을 깨달았습니다. 이는 소스 버퍼에서 한 번에 8 바이트를 읽는 것을 의미합니다.

어쨌든 이것은 내가 끝내는 것입니다 ...

var buff = new Buffer("40100000000000004014000000000000", "hex");
var ab = new ArrayBuffer(buff.length);
var view = new Float64Array(ab);

var viewIndex = 0;
for (var bufferIndex=0;bufferIndex<buff.length;bufferIndex=bufferIndex+8)            {

    view[viewIndex] = buff.readDoubleLE(bufferIndex);
    viewIndex++;
}

Martin Thomson의 답변이 Uint8Array를 사용하는 이유는 요소의 크기와 무관합니다. Buffer.read*방법은, 모든 느리다.
ZachB

여러 유형의 배열 뷰는 동일한 메모리를 사용하여 동일한 ArrayBuffer를 참조 할 수 있습니다. 버퍼의 각 값은 1 바이트이므로 요소 크기가 1 바이트 인 배열에 넣어야합니다. Martin의 메소드를 사용한 다음 생성자에서 동일한 배열 버퍼를 사용하여 새 Float64Array를 만들 수 있습니다.
ZachB

0

이 프록시는 복사본없이 버퍼를 TypedArray 중 하나로 노출합니다. :

https://www.npmjs.com/package/node-buffer-as-typedarray

LE에서만 작동하지만 BE로 쉽게 이식 할 수 있습니다. 또한 실제로 이것이 얼마나 효율적인지 테스트하지 않아도됩니다.


1
이 링크가 질문에 대답 할 수 있지만 여기에 답변의 필수 부분을 포함시키고 참조 할 링크를 제공하는 것이 좋습니다. 링크 된 페이지가 변경되면 링크 전용 답변이 무효화 될 수 있습니다
koceeng

2
내 말은 그리 공식적으로 들리지 않을 수도 있지만 솔루션을 재현하기에 충분한 정보를 제공합니다. 이 솔루션은 JavaScript Proxy Object를 사용하여 TypedArray에서 사용하는 getter 및 setter로 기본 NodeJS 버퍼를 랩핑합니다. 따라서 버퍼 인스턴스는 Typed Array 인터페이스가 필요한 모든 라이브러리와 호환됩니다. 이것은 원래 포스터가 기대했던 해답이지만, 학계 / 기업의 용어에 맞지 않기 때문에 자유롭게 무시하십시오. 내가 관심이 있는지보십시오.
Dlabz


-1

NodeJS는 한 시점에서 (v0.6.x라고 생각합니다) ArrayBuffer를 지원했습니다. 여기 base64 인코딩 및 디코딩을위한 작은 라이브러리를 만들었지 만 v0.7로 업데이트 한 후 NodeJS에서 테스트가 실패합니다. 나는 이것을 정상화하는 것을 만들려고 생각하고 있지만 그때까지는 Node의 네이티브를 Buffer사용해야 한다고 생각 합니다.


-6

이미 노드를 버전 5.0.0으로 업데이트했으며 다음과 같이 작업합니다.

function toArrayBuffer(buffer){
    var array = [];
    var json = buffer.toJSON();
    var list = json.data

    for(var key in list){
        array.push(fixcode(list[key].toString(16)))
    }

    function fixcode(key){
        if(key.length==1){
            return '0'+key.toUpperCase()
        }else{
            return key.toUpperCase()
        }
    }

    return array
}

vhd 디스크 이미지를 확인하는 데 사용합니다.


이것은 Buffer / ArrayBuffer로 /에서 변환하는 일반적인 방법이 아닌 특수하고 느린 직렬화 기반 방법처럼 보입니까?
ZachB

@ZachB V5.0.0 + [only] = =의 일반적인 방법입니다.
Miguel Valentine

toArrayBuffer(new Buffer([1,2,3]))-> ['01', '02', '03']-정수 / 바이트가 아닌 문자열 배열을 반환합니다.
ZachB

@ZachB 반환 배열-> 반환 목록. 나는 stdout을 위해 int-> string을 수정한다.
Miguel Valentine

이 경우는 같은이다 stackoverflow.com/a/19544002/1218408 에서 필요한 바이트 오프셋을 확인하지 않고 정지하고 stackoverflow.com/a/31394257/1218408 .
ZachB
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.