Konversi biner NodeJS Buffer ke JavaScript ArrayBuffer

133

Bagaimana saya bisa mengubah buffer biner NodeJS menjadi JavaScript ArrayBuffer?

Drake Amara
sumber
1
Saya ingin tahu mengapa Anda perlu melakukan ini?
Chris Biscardi
14
contoh yang baik adalah menulis perpustakaan yang berfungsi dengan File di browser dan juga untuk file NodeJS?
fbstj
1
atau menggunakan perpustakaan browser dalam NodeJS
OrangeDog
1
Alasan lain adalah bahwa float mengambil terlalu banyak byte RAM ketika disimpan dalam sebuah Array. Jadi untuk menyimpan banyak pelampung yang Anda butuhkan di Float32Arraymana dibutuhkan 4 byte. Dan jika Anda ingin serialisasi cepat dari float ke file yang Anda butuhkan Buffer, karena serialisasi ke JSON membutuhkan waktu lama.
nponeccop
Saya ingin tahu persis hal yang sama untuk mengirim data umum menggunakan WebRTC dan sulit dipercaya bahwa begitu banyak jawaban di sini memiliki begitu banyak suka, tetapi tidak menjawab pertanyaan yang sebenarnya ...
Felix Crazzolara

Jawaban:

134

Instance Bufferjuga instanceUint8Array dalam node.js 4.x dan lebih tinggi. Dengan demikian, solusi paling efisien adalah mengakses buf.bufferproperti secara langsung, seperti https://stackoverflow.com/a/31394257/1375574 . Konstruktor Buffer juga mengambil argumen ArrayBufferView jika Anda perlu pergi ke arah lain.

Perhatikan bahwa ini tidak akan membuat salinan, yang berarti bahwa menulis ke ArrayBufferView apa pun akan menulis hingga contoh Buffer asli.


Dalam versi yang lebih lama, node.js memiliki kedua ArrayBuffer sebagai bagian dari v8, tetapi kelas Buffer menyediakan API yang lebih fleksibel. Untuk membaca atau menulis ke ArrayBuffer, Anda hanya perlu membuat tampilan dan menyalinnya.

Dari Buffer ke 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;
}

Dari ArrayBuffer ke 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;
}
Martin Thomson
sumber
5
Saya juga merekomendasikan Anda untuk mengoptimalkan ini dengan menyalin bilangan bulat jika mungkin menggunakan DataView. Sampai size&0xfffffffe, salin integer 32-bit, lalu, jika ada 1 byte tersisa, salin integer 8-bit, jika 2 byte, salin integer 16-bit, dan jika 3 byte, salin integer 16-bit dan 8-bit.
Triang3l
3
Lihat jawaban kraag22 untuk implementasi yang lebih sederhana dari setengahnya.
OrangeDog
Telah menguji Buffer -> ArrayBuffer dengan modul yang ditujukan untuk penggunaan browser dan berfungsi dengan baik. Terima kasih!
pospi
3
Kenapa abdikembalikan? Tidak ada yang dilakukan ab? Saya selalu mendapatkan {}hasilnya.
Andi Giga
1
' slice()Metode mengembalikan yang baru ArrayBufferyang isinya salinan ArrayBufferbyte ini dari awal, inklusif, hingga akhir, eksklusif.' - MDNArrayBuffer.prototype.slice()
Константин Ван
61

Tidak ada dependensi, tercepat, Node.js 4.x dan yang lebih baru

Buffers adalah Uint8Arrays, jadi Anda hanya perlu mengiris (menyalin) wilayah dukungannya 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);

The slicehal-hal dan offset diperlukan karena kecil Buffers (kurang dari 4 kB secara default, setengah ukuran kolam ) dapat dilihat pada bersama ArrayBuffer. Tanpa mengiris, Anda bisa berakhir dengan ArrayBufferberisi data dari yang lain Buffer. Lihat penjelasan dalam dokumen .

Jika pada akhirnya Anda membutuhkan TypedArray, Anda dapat membuatnya tanpa menyalin data:

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

Tidak ada dependensi, kecepatan sedang, versi Node.js apa pun

Gunakan jawaban Martin Thomson , yang berjalan dalam O (n) waktu. (Lihat juga balasan saya untuk komentar pada jawabannya tentang non-optimisasi. Menggunakan DataView lambat. Bahkan jika Anda perlu membalik byte, ada cara yang lebih cepat untuk melakukannya.)

Ketergantungan, cepat, Node.js ≤ 0,12 atau iojs 3.x

Anda dapat menggunakan https://www.npmjs.com/package/memcpy untuk menuju salah satu arah (Buffer ke ArrayBuffer dan kembali). Ini lebih cepat daripada jawaban lain yang diposting di sini dan merupakan perpustakaan yang ditulis dengan baik. Simpul 0,12 hingga iojs 3.x memerlukan garpu ngossen (lihat ini ).

ZachB
sumber
Itu tidak mengkompilasi lagi simpul> 0,12
Pawel Veselov
1
Gunakan garpu ngossen : github.com/dcodeIO/node-memcpy/pull/6 . Lihat juga jawaban baru saya jika Anda menggunakan simpul 4+.
ZachB
Di mana .byteLengthdan .byteOffsetdidokumentasikan?
Константин Ван
1
var ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);menyelamatkan hari saya
Alexey Sh.
56

"Dari ArrayBuffer ke Buffer" dapat dilakukan dengan cara ini:

var buffer = Buffer.from( new Uint8Array(ab) );
kraag22
sumber
27
Itu kebalikan dari apa yang diinginkan OP.
Alexander Gonchiy
43
Tetapi itulah yang saya inginkan dari googling masalah saya dan senang saya telah menemukan solusinya.
Maciej Krawczyk
27

Cara yang lebih cepat untuk menulisnya

var arrayBuffer = new Uint8Array(nodeBuffer).buffer;

Namun, ini tampaknya berjalan kira-kira 4 kali lebih lambat daripada fungsi toArrayBuffer yang disarankan pada buffer dengan 1024 elemen.

David Fooks
sumber
3
Tambahan terlambat: @trevnorris mengatakan "mulai di [V8] 4.3 Buffer didukung oleh Uint8Array", jadi mungkin ini lebih cepat sekarang ...
ChrisV
Lihat jawaban saya untuk cara aman untuk melakukan ini.
ZachB
3
Mengujinya dengan v5.6.0 dan itu yang tercepat
daksh_019
1
Ini hanya berfungsi karena instance Bufferjuga instance Uint8Arraydalam Node.js 4.x dan lebih tinggi. Untuk versi Node.js yang lebih rendah Anda harus mengimplementasikan suatu toArrayBufferfungsi.
Benny Neugebauer
14

1. A Bufferhanyalah tampilan untuk melihat ke dalam ArrayBuffer.

A Buffer, pada kenyataannya, adalah a FastBuffer, yang extends(mewarisi dari) Uint8Array, yang merupakan tampilan satuan oktet ("pengakses parsial") dari memori aktual, an 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. Ukuran ArrayBufferdan ukuran tampilan dapat bervariasi.

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

Dengan Buffer.from(arrayBuffer[, byteOffset[, length]]), Anda dapat membuat Bufferdengan menentukan dasarnya ArrayBufferdan posisi serta ukuran tampilan.

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.

Alasan # 2: FastBufferalokasi memori.

Ini mengalokasikan memori dalam dua cara berbeda tergantung pada ukurannya.

  • Jika ukurannya kurang dari setengah ukuran kolam memori dan bukan 0 ("kecil") : itu menggunakan kolam memori untuk menyiapkan memori yang diperlukan.
  • Lain : itu menciptakan dedicated ArrayBufferyang persis sesuai dengan memori yang diperlukan.
  📜 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));
}

Apa yang Anda maksud dengan " kumpulan memori ?"

Kumpulan memori adalah blok memori pra-alokasi ukuran tetap untuk menjaga potongan memori berukuran kecil untuk Buffers. Menggunakannya membuat potongan memori ukuran kecil tetap rapat, sehingga mencegah fragmentasi yang disebabkan oleh manajemen yang terpisah (alokasi dan deallokasi) dari potongan memori ukuran kecil.

Dalam kasus ini, kumpulan memori adalah ArrayBuffers yang ukurannya 8 KiB secara default, yang ditentukan dalam Buffer.poolSize. Ketika akan menyediakan potongan memori ukuran kecil untuk a Buffer, itu memeriksa apakah kumpulan memori terakhir memiliki cukup memori yang tersedia untuk menangani ini; jika demikian, itu menciptakan Bufferyang “dilihat” potongan parsial diberikan dari kolam memori, jika tidak, itu menciptakan kolam memori baru dan sebagainya.


Anda dapat mengakses dasar ArrayBufferdari a Buffer. The Buffer's bufferproperti (yaitu, diwarisi dari Uint8Array) memegang itu. Sebuah “kecil” Buffer 's bufferproperti merupakan ArrayBufferyang mewakili seluruh kolam memori. Jadi dalam hal ini, ArrayBufferdan Bufferukurannya bervariasi.

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. Jadi kita perlu mengekstrak memori itu " dilihat ."

Sebuah ArrayBufferadalah tetap dalam ukuran, sehingga kita perlu untuk mengambil keluar dengan membuat salinan bagian. Untuk melakukan hal ini, kita menggunakan Buffer's byteOffsetproperti dan lengthproperti , yang diwarisi dari Uint8Array, dan yang ArrayBuffer.prototype.slicemetode , yang membuat salinan bagian dari ArrayBuffer. The slice()Metode -ing disini terinspirasi oleh @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. Peningkatan kinerja

Jika Anda menggunakan hasil sebagai hanya-baca, atau apa-apa untuk mengubah masukan Buffers'isi , Anda dapat menghindari yang tidak perlu menyalin memori.

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
Константин Ван
sumber
4
Ini semua baik dan bagus ... tetapi apakah Anda benar-benar menjawab pertanyaan OP? Jika Anda melakukannya, itu dimakamkan ...
Tustin2121
Jawaban bagus! Dalam obtain_arraybuffer: buf.buffer.subarraysepertinya tidak ada. Apakah maksud Anda di buf.buffer.slicesini?
hari produktif
@everydayproductive Terima kasih. Seperti yang Anda lihat di riwayat edit, saya benar-benar menggunakan ArrayBuffer.prototype.slicedan kemudian memodifikasinya Uint8Array.prototype.subarray. Oh, dan aku salah melakukannya. Mungkin agak bingung saat itu. Semua baik-baik saja sekarang, terima kasih.
Константин Ван
12

Menggunakan paket NPM sangat baik sebagai berikut: to-arraybuffer.

Atau, Anda bisa menerapkannya sendiri. Jika buffer Anda dipanggil buf, lakukan ini:

buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)
Feross
sumber
21
Joyent mengeluarkannya
aleclarson
1

Anda dapat menganggap ArrayBuffersebagai diketik Buffer.

Sebuah ArrayBufferkarena itu selalu membutuhkan jenis (yang disebut "Array Buffer View"). Biasanya, Array Buffer View memiliki tipe Uint8Arrayatau Uint16Array.

Ada artikel bagus dari Renato Mangini tentang konversi antara ArrayBuffer dan String .

Saya telah merangkum bagian-bagian penting dalam contoh kode (untuk Node.js). Ini juga menunjukkan bagaimana mengkonversi antara yang diketik ArrayBufferdan yang tidak diketik 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!"
Benny Neugebauer
sumber
0

Saya mencoba di atas untuk Float64Array dan itu tidak berhasil.

Saya akhirnya menyadari bahwa benar-benar data yang perlu dibaca 'KEPADA' tampilan dalam potongan yang benar. Ini berarti membaca 8 byte sekaligus dari Buffer sumber.

Pokoknya inilah yang akhirnya saya dapatkan dengan ...

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++;
}
Exitos
sumber
Itu sebabnya jawaban Martin Thomson menggunakan Uint8Array - itu agnostik dengan ukuran elemen. The Buffer.read*metode semua lambat, juga.
ZachB
Beberapa tampilan array yang diketik dapat merujuk ArrayBuffer yang sama menggunakan memori yang sama. Setiap nilai dalam Buffer adalah satu byte, jadi Anda harus memasukkannya ke dalam array dengan ukuran elemen 1 byte. Anda dapat menggunakan metode Martin, kemudian membuat Float64Array baru menggunakan arraybuffer yang sama di konstruktor.
ZachB
0

Proxy ini akan mengekspos buffer sebagai salah satu dari TypedArrays, tanpa salinan apa pun. :

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

Ini hanya bekerja pada LE, tetapi dapat dengan mudah dipindahkan ke BE. Juga, tidak pernah benar-benar menguji seberapa efisien ini.

Dlabz
sumber
1
Meskipun tautan ini dapat menjawab pertanyaan, lebih baik untuk memasukkan bagian-bagian penting dari jawaban di sini dan memberikan tautan untuk referensi. Jawaban khusus tautan dapat menjadi tidak valid jika halaman tertaut berubah
koceeng
2
Kata-kata saya mungkin kedengarannya tidak terlalu resmi, tetapi itu memberikan informasi yang cukup untuk menciptakan kembali solusinya. Solusinya bergantung pada JavaScript Proxy Object untuk membungkus Buffer NodeJS asli dengan getter dan setter yang digunakan oleh TypedArrays. Ini membuat instance Buffer kompatibel dengan pustaka apa pun yang memerlukan antarmuka Typed Array. Ini adalah jawaban yang diharapkan oleh poster asli, tetapi jangan ragu untuk mengabaikannya karena tidak sesuai dengan istilah akademis / perusahaan Anda. Lihat apakah aku peduli.
Dlabz
-1

NodeJS, pada satu titik (saya pikir itu v0.6.x) mendapat dukungan ArrayBuffer. Saya membuat perpustakaan kecil untuk encoding dan decoding base64 di sini , tetapi sejak memperbarui ke v0.7, tes (pada NodeJS) gagal. Saya sedang berpikir untuk menciptakan sesuatu yang menormalkan ini, tetapi sampai saat itu, saya kira asli Node Bufferharus digunakan.

arunjitsingh
sumber
-6

Saya sudah memperbarui simpul saya ke Versi 5.0.0 Dan saya bekerja dengan ini:

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
}

Saya menggunakannya untuk memeriksa image disk VHD saya.

Miguel Valentine
sumber
Ini terlihat seperti metode berbasis serialisasi khusus (dan lambat), bukan metode umum untuk mengkonversi ke / dari Buffer / ArrayBuffer?
ZachB
@ZachB itu adalah metode umum untuk V5.0.0 + [hanya] = =.
Miguel Valentine
toArrayBuffer(new Buffer([1,2,3]))-> ['01', '02', '03']- ini mengembalikan array string, bukan bilangan bulat / byte.
ZachB
@ZachB return array -> daftar pengembalian. saya memperbaiki int-> string untuk stdout
Miguel Valentine
Dalam hal ini sama dengan stackoverflow.com/a/19544002/1218408 , dan masih tanpa perlu byte offset memeriksa dalam stackoverflow.com/a/31394257/1218408 .
ZachB