バイナリNodeJSバッファーをJavaScript ArrayBufferに変換する


133

NodeJSバイナリバッファーをJavaScript ArrayBufferに変換するにはどうすればよいですか?


1
なぜこれを行う必要があるのか​​、知りたいのですが。
Chris Biscardi 2011

14
良い例は、ブラウザでFileを使用して、NodeJSファイルでも機能するライブラリを作成することでしょうか?
fbstj 2012年

1
またはNodeJSでブラウザライブラリを使用
OrangeDog

1
もう1つの理由は、floatをに格納すると、RAMのバイト数が多すぎるためですArray。したがって、多くの浮動小数点Float32Array数を格納するには、4バイトかかる場所が必要です。また、BufferJSONへのシリアル化には時間がかかるため、これらのフロートをファイルにすばやくシリアル化する場合は、が必要です。
nponeccop 2013年

WebRTCを使用して一般的なデータを送信するのとまったく同じことを知りたいのですが、ここに非常に多くの回答が含まれているとは信じられませんが、実際の質問には回答しません...
Felix Crazzolara

回答:


134

のインスタンスはBufferUint8Array、node.js 4.x以降のインスタンスでもあります。したがって、最も効率的なソリューションはbuf.bufferhttps://stackoverflow.com/a/31394257/1375574に従って、プロパティに直接アクセスすることです。他の方向に進む必要がある場合、BufferコンストラクターはArrayBufferView引数も受け取ります。

これはコピーを作成しないことに注意してください。つまり、ArrayBufferViewへの書き込みは、元のBufferインスタンスへの書き込みになります。


以前のバージョンでは、node.jsはv8の一部としてArrayBufferの両方を備えていますが、Bufferクラスはより柔軟なAPIを提供します。ArrayBufferを読み書きするには、ビューを作成してコピーするだけです。

バッファから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から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;
}

5
また、可能な場合はDataViewを使用して整数をコピーすることにより、これを最適化することをお勧めします。までsize&0xfffffffe、32ビット整数をコピーし、残りが1バイトの場合は8ビット整数をコピーし、2バイトの場合は16ビット整数をコピーし、3バイトの場合は16ビットと8ビット整数をコピーします。
Triang3l 2013

3
この半分のより簡単な実装については、kraag22の回答を参照してください。
OrangeDog 2013年

ブラウザでの使用を目的としたモジュールでBuffer-> ArrayBufferをテストしましたが、見事に機能しています。ありがとう!
pospi

3
なぜab返されるのですか?何もしていabませんか?私はいつも{}結果として得ます。
アンディギガ

1
'このslice()メソッドは、ArrayBuffer内容ArrayBufferの先頭から両端までのこのバイトのコピーを含むnew を返します。- MDNArrayBuffer.prototype.slice()
КонстантинВан

61

依存関係なし、最速、Node.js 4.x以降

BuffersはUint8Arraysなので、バッキングのその領域をスライス(コピー)するだけです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(デフォルト、半分未満の4kBのプールサイズは)共有上でビューすることができます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を使用して、どちらの方向にも移動できます(バッファーからArrayBufferへ、およびその逆)。ここに掲載されている他の回答よりも速く、よく書かれたライブラリです。ノード0.12からiojs 3.xまでは、ngossenのフォークが必要です(これを参照)。


ノード> 0.12を再度コンパイルしない
Pawel Veselov

1
ngossenのフォーク:github.com/dcodeIO/node-memcpy/pull/6を使用します。ノード4+を使用している場合は、新しい回答も参照してください。
ZachB

どこた.byteLengthし、.byteOffset文書化?
КонстантинВан


1
var ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);私の日を救った
アレクセイSh。

56

「ArrayBufferからBufferへ」は次のように実行できます。

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、2015

3
v5.6.0でテストし、最も高速でした
daksh_019 '11

1
これが機能するのは、 BufferインスタンスはUint8ArrayNode.js 4.x以降ののインスタンスでもある。Node.jsの下位バージョンでは、toArrayBuffer関数を実装する必要があります。
Benny Neugebauer 2017年

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]])、あなたが作成することができ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.

理由#2:FastBufferのメモリ割り当て。

サイズに応じて、2つの異なる方法でメモリを割り当てます。

  • サイズがメモリプールのサイズの半分より小さい場合 0でない場合(「小さい」)メモリプールを使用して必要なメモリを準備します。
  • そうしないとArrayBuffer必要なメモリに正確に適合する専用を作成します。
  📜/lib/buffer.js#L306-L320 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);
  }
}
  📜/lib/buffer.js#L98-L100 9.4.0
function createUnsafeBuffer(size) {
  return new FastBuffer(createUnsafeArrayBuffer(size));
}

メモリプール」とはどういう意味ですか?

メモリプールは、固定サイズであり、事前に割り当てられたため、小さなサイズのメモリチャンクを保持するためのメモリブロックBufferS。これを使用すると、小さなサイズのメモリチャンクが密に保持されるため、個別の管理(割り当てと割り当て解除)によって引き起こされる断片化がされます。

この場合、メモリプールはArrayBufferデフォルトで8 KiBのサイズで、で指定されていBuffer.poolSizeます。に小さなサイズのメモリチャンクを提供するBuffer場合、最後のメモリプールにこれを処理するのに十分な使用可能メモリがあるかどうかをチェックします。もしそうなら、それが作成しBuffer、その「意見を」は、メモリプールの指定された部分的なチャンクます。それ以外の場合は、新しいメモリプールを作成します。


の基礎ArrayBufferにアクセスできますBufferプロパティ(から継承されている、)それを保持しています。「小さな」プロパティがあるメモリプール全体を表しています。したがって、この場合、およびはサイズが異なります。BufferbufferUint8Array 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サイズは固定なので、我々は一部のコピーを作成することにより、それを抽出する必要があります。これを行うには、から継承される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

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_arraybufferbuf.buffer.subarray存在していないようです。もしかしてbuf.buffer.sliceここ?
毎日生産的

@everydayproductiveありがとうございます。編集履歴で確認できるように、実際に使用しArrayBuffer.prototype.slice、後でに変更しましたUint8Array.prototype.subarray。ああ、私はそれを間違った。おそらく、当時は少し混乱していました。あなたのおかげで、今はすべて順調です。
КонстантинВан


1

ArrayBufferは型付きと考えることができますBuffer

ArrayBufferしたがって、常にタイプ(いわゆる「アレイバッファービュー」)を必要とします。通常、配列バッファビューには、Uint8ArrayまたはのタイプがありUint16Arrayます。

ArrayBufferとString間の変換については、Renato Manginiによる優れた記事があります。

(Node.jsの)コード例で重要な部分をまとめました。また、型付きArrayBufferと型なしの間の変換方法も示します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 2015

0

このプロキシは、コピーなしで、バッファをTypedArrayのいずれかとして公開します。:

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

LEでのみ機能しますが、BEに簡単に移植できます。また、これがどれほど効率的かを実際にテストする必要はありません。


1
このリンクで質問に答えることができますが、回答の重要な部分をここに含め、参照用のリンクを提供することをお勧めします。リンクされたページが変更されると、リンクのみの回答が無効になる可能性があります
koceeng

2
私の言い回しはあまり正式に聞こえないかもしれませんが、ソリューションを再現するのに十分な情報を提供します。このソリューションは、JavaScriptプロキシオブジェクトに依存して、ネイティブNodeJSバッファーをTypedArraysで使用されるゲッターとセッターでラップします。これにより、Bufferインスタンスは、型付き配列インターフェイスを必要とするすべてのライブラリと互換性があります。これは、元のポスターが望んでいた答えですが、それがあなたの学問/企業用語に合わないので、遠慮なく却下してください。私が気にするかどうか見てください。
Dlabz 2017

0

これに非常に便利なnpmパッケージがあります:https : buffer //github.com/feross/buffer

ノードのバッファAPIと100%同一のAPIを提供し、以下を許可しようとします。

そしていくつか。


-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、2015

@ZachB V5.0.0 + [のみ] = =のジェネリックメソッドです。
ミゲルバレンタイン

toArrayBuffer(new Buffer([1,2,3]))-> ['01', '02', '03']-これは整数/バイトではなく、文字列の配列を返します。
ZachB

@ZachB戻り配列->戻りリスト。stdoutのint-> stringを修正します
ミゲルバレンタイン

その場合、それは同じだstackoverflow.com/a/19544002/1218408で必要なバイトオフセットチェックなしまだ、およびstackoverflow.com/a/31394257/1218408
ZachB
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.