159 votes

Convertir un Buffer NodeJS binaire en ArrayBuffer JavaScript

Comment puis-je convertir un tampon binaire NodeJS en un ArrayBuffer JavaScript ?

1 votes

Je suis curieux de savoir pourquoi vous avez besoin de faire ça ?

16 votes

Un bon exemple serait d'écrire une bibliothèque qui fonctionne avec les fichiers dans les navigateurs et aussi pour les fichiers NodeJS ?

1 votes

Ou en utilisant une bibliothèque de navigateur dans NodeJS

169voto

Martin Thomson Points 733

Instances de Buffer sont également des instances de Uint8Array dans node.js 4.x et plus. Ainsi, la solution la plus efficace est d'accéder au fichier buf.buffer directement, conformément à la https://stackoverflow.com/a/31394257/1375574 . Le constructeur de Buffer prend également un argument ArrayBufferView si vous avez besoin d'aller dans l'autre sens.

Notez que cela ne créera pas de copie, ce qui signifie que les écritures dans n'importe quel ArrayBufferView seront écrites dans l'instance de Buffer originale.


Dans les versions antérieures, node.js dispose des deux ArrayBuffer dans le cadre de la v8, mais la classe Buffer fournit une API plus flexible. Pour lire ou écrire dans un ArrayBuffer, il suffit de créer une vue et de la copier à travers.

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

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

6 votes

Je vous recommande également d'optimiser cette opération en copiant des nombres entiers lorsque cela est possible en utilisant DataView. Jusqu'à ce que size&0xfffffffe Si le nombre d'octets restants est inférieur à 1, copier un entier de 8 bits, si 2 octets, copier un entier de 16 bits, et si 3 octets, copier un entier de 16 bits et un entier de 8 bits.

3 votes

Voir la réponse de kraag22 pour une implémentation plus simple de la moitié de ceci.

0 votes

J'ai testé Buffer -> ArrayBuffer avec un module destiné à être utilisé dans un navigateur et cela fonctionne parfaitement. Merci de votre compréhension.

81voto

ZachB Points 1322

Pas de dépendances, plus rapide, Node.js 4.x et ultérieur

Buffer sont Uint8Array il suffit donc de découper (copier) sa région dans la base de données. 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);

El slice et les trucs compensatoires sont requis parce que les petits Buffer (moins de 4 ko par défaut, soit la moitié de la capacité de l'entreprise). taille du bassin ) peuvent être vues sur une ArrayBuffer . Sans trancher, vous pouvez vous retrouver avec une ArrayBuffer contenant des données provenant d'un autre Buffer . Voir explication dans les docs .

Si vous avez finalement besoin d'un TypedArray vous pouvez en créer un sans copier les données :

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

Pas de dépendances, vitesse modérée, toute version de Node.js

Utilisez La réponse de Martin Thomson qui fonctionne en O(n) temps. (Voir aussi mes réponses aux commentaires sur sa réponse concernant les non-optimisations. Utiliser un DataView est lent. Même si vous avez besoin de retourner des octets, il existe des moyens plus rapides de le faire).

Dépendance, rapide, Node.js 0.12 ou iojs 3.x

Vous pouvez utiliser https://www.npmjs.com/package/memcpy pour aller dans les deux sens (Buffer vers ArrayBuffer et inversement). C'est plus rapide que les autres réponses postées ici et c'est une bibliothèque bien écrite. Node 0.12 jusqu'à iojs 3.x nécessite le fork de ngossen (cf. ce ).

0 votes

Il ne compile plus node > 0.12

1 votes

Utiliser la fourchette de ngossen : github.com/dcodeIO/node-memcpy/pull/6 . Voir aussi ma nouvelle réponse si vous utilisez node 4+.

0 votes

Où étaient les .byteLength y .byteOffset documentée ?

72voto

kraag22 Points 698

"De ArrayBuffer à Buffer" pourrait se faire de cette façon :

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

35 votes

C'est le contraire de ce que voulait OP.

60 votes

Mais c'est ce que je voulais en cherchant mon problème sur Google et je suis content d'avoir trouvé la solution.

31voto

David Fooks Points 135

Une façon plus rapide de l'écrire

var arrayBuffer = new Uint8Array(nodeBuffer).buffer;

Cependant, cette fonction semble être environ 4 fois plus lente que la fonction toArrayBuffer suggérée sur un tampon de 1024 éléments.

3 votes

Ajout tardif : @trevnorris dit "à partir de [V8] 4.3 Les tampons sont soutenus par Uint8Array", il est donc possible que cela soit plus rapide maintenant...

0 votes

Voir ma réponse pour la manière sûre de le faire.

3 votes

Je l'ai testé avec la version 5.6.0 et c'était le plus rapide.

17voto

1. A Buffer est juste un voir pour avoir examiné un ArrayBuffer .

A Buffer en fait, c'est un FastBuffer qui extends (hérite de) Uint8Array qui est une unité d'octet voir (" accesseur partiel ") de la mémoire réelle, un 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;

2. La taille d'un ArrayBuffer et la taille de son voir peut varier.

Raison n°1 : Buffer.from(arrayBuffer[, byteOffset[, length]]) .

Avec Buffer.from(arrayBuffer[, byteOffset[, length]]) vous pouvez créer un Buffer avec la spécification de son sous-jacent ArrayBuffer ainsi que la position et la taille de la vue.

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.

Raison n° 2 : FastBuffer de l'allocation de mémoire.

Il alloue la mémoire de deux manières différentes en fonction de la taille.

  • Si la taille est inférieure à la moitié de la taille d'une réserve de mémoire et n'est pas égal à 0 ("petit") il fait appel à une réserve de mémoire pour préparer la mémoire requise.
  • Else il crée un espace dédié ArrayBuffer qui correspond exactement à la mémoire requise.

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

  /lib/buffer.js#L98-L100 Node.js 9.4.0

function createUnsafeBuffer(size) {
  return new FastBuffer(createUnsafeArrayBuffer(size));
}

Que voulez-vous dire par un " réserve de mémoire ?"

A réserve de mémoire est une taille fixe préaffecté bloc de mémoire permettant de conserver des morceaux de mémoire de petite taille pour Buffer s. En l'utilisant, on maintient les petits morceaux de mémoire serrés les uns contre les autres, ce qui empêche les fragmentation causée par la gestion séparée (allocation et désallocation) de morceaux de mémoire de petite taille.

Dans ce cas, les pools de mémoire sont ArrayBuffer dont la taille est de 8 KiB par défaut, ce qui est spécifié dans le fichier Buffer.poolSize . Quand il s'agit de fournir un morceau de mémoire de petite taille pour une Buffer il vérifie si le dernier pool de mémoire a suffisamment de mémoire disponible pour gérer cette opération ; si c'est le cas, il crée un fichier Buffer que "vues" le morceau partiel donné du pool de mémoire, sinon, il crée un nouveau pool de mémoire et ainsi de suite.


Vous pouvez accéder à la base de données ArrayBuffer d'un Buffer . El Buffer 's buffer propriété (c'est-à-dire hérité de Uint8Array ) le tient. A "petit" Buffer 's buffer est un ArrayBuffer qui représente l'ensemble de la mémoire. Donc dans ce cas, le ArrayBuffer y el Buffer varie en taille.

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. Nous devons donc extraire la mémoire qu'il " vues ."

Un site ArrayBuffer est de taille fixe, nous devons donc l'extraire en faisant une copie de la pièce. Pour ce faire, nous utilisons Buffer 's byteOffset propriété y length propriété qui sont héritées de Uint8Array y le site ArrayBuffer.prototype.slice méthode qui fait une copie d'une partie d'un fichier ArrayBuffer . El slice() -La méthode utilisée ici a été inspirée par @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. Amélioration des performances

Si vous devez utiliser les résultats en lecture seule, ou si vous pouvez modifier l'entrée Buffer Contenu des "s vous pouvez éviter de copier inutilement la mémoire.

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.bytLength)
    {
        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 votes

C'est bien beau tout ça... mais avez-vous vraiment répondu à la question de l'OP ? Si c'est le cas, elle est enterrée...

0 votes

Excellente réponse ! En obtain_arraybuffer : buf.buffer.subarray ne semble pas exister. Vouliez-vous dire buf.buffer.slice ici ?

0 votes

@everydayproductive Merci. Comme vous pouvez le voir dans l'historique des modifications, j'ai en fait utilisé ArrayBuffer.prototype.slice et l'a ensuite modifié en Uint8Array.prototype.subarray . Oh, et je me suis trompé. J'ai probablement été un peu confus à l'époque. Tout va bien maintenant grâce à vous.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X