Cette page est destinée à servir d'introduction au travail avec des données binaires en JavaScript. Bun implémente un certain nombre de types de données et d'utilitaires pour travailler avec des données binaires, dont la plupart sont des standards Web. Toutes les API spécifiques à Bun seront notées comme telles.
Ci-dessous se trouve un "pense-bête" rapide qui sert également de table des matières. Cliquez sur un élément dans la colonne de gauche pour accéder à cette section.
| Classe | Description |
|---|---|
TypedArray | Une famille de classes qui fournissent une interface de type Array pour interagir avec des données binaires. Comprend Uint8Array, Uint16Array, Int8Array, et plus encore. |
Buffer | Une sous-classe de Uint8Array qui implémente un large éventail de méthodes de commodité. Contrairement aux autres éléments de ce tableau, il s'agit d'une API Node.js (que Bun implémente). Elle ne peut pas être utilisée dans le navigateur. |
DataView | Une classe qui fournit une API get/set pour écrire un certain nombre d'octets dans un ArrayBuffer à un décalage d'octets particulier. Souvent utilisé pour lire ou écrire des protocoles binaires. |
Blob | Un blob binaire en lecture seule représentant généralement un fichier. Possède un type MIME, une size, et des méthodes pour convertir en ArrayBuffer, ReadableStream, et string. |
File | Une sous-classe de Blob qui représente un fichier. Possède un name et un horodatage lastModified. Il y a un support expérimental dans Node.js v20. |
BunFile | Bun uniquement. Une sous-classe de Blob qui représente un fichier chargé paresseusement sur le disque. Créé avec Bun.file(path). |
ArrayBuffer et vues
Jusqu'en 2009, il n'existait aucun moyen natif dans le langage de stocker et de manipuler des données binaires en JavaScript. ECMAScript v5 a introduit un éventail de nouveaux mécanismes pour cela. Le bloc de construction le plus fondamental est ArrayBuffer, une structure de données simple qui représente une séquence d'octets en mémoire.
// ce buffer peut stocker 8 octets
const buf = new ArrayBuffer(8);Malgré son nom, ce n'est pas un tableau et ne prend en charge aucune des méthodes et opérateurs de tableau auxquels on pourrait s'attendre. En fait, il n'y a aucun moyen de lire ou d'écrire directement des valeurs depuis un ArrayBuffer. Il y a très peu de choses que vous pouvez faire avec l'un d'eux sauf vérifier sa taille et créer des "tranches" à partir de celui-ci.
const buf = new ArrayBuffer(8);
buf.byteLength; // => 8
const slice = buf.slice(0, 4); // retourne un nouvel ArrayBuffer
slice.byteLength; // => 4Pour faire quelque chose d'intéressant, nous avons besoin d'une construction connue sous le nom de "vue". Une vue est une classe qui enveloppe une instance ArrayBuffer et vous permet de lire et de manipuler les données sous-jacentes. Il existe deux types de vues : les typed arrays et DataView.
DataView
La classe DataView est une interface de niveau inférieur pour lire et manipuler les données dans un ArrayBuffer.
Ci-dessous, nous créons un nouveau DataView et définissons le premier octet à 3.
const buf = new ArrayBuffer(4);
// [0b00000000, 0b00000000, 0b00000000, 0b00000000]
const dv = new DataView(buf);
dv.setUint8(0, 3); // écrit la valeur 3 au décalage d'octet 0
dv.getUint8(0); // => 3
// [0b00000011, 0b00000000, 0b00000000, 0b00000000]Maintenant, écrivons un Uint16 au décalage d'octet 1. Cela nécessite deux octets. Nous utilisons la valeur 513, qui est 2 * 256 + 1 ; en octets, c'est 00000010 00000001.
dv.setUint16(1, 513);
// [0b00000011, 0b00000010, 0b00000001, 0b00000000]
console.log(dv.getUint16(1)); // => 513Nous avons maintenant assigné une valeur aux trois premiers octets de notre ArrayBuffer sous-jacent. Même si les deuxième et troisième octets ont été créés en utilisant setUint16(), nous pouvons toujours lire chacun de ses octets composants en utilisant getUint8().
console.log(dv.getUint8(1)); // => 2
console.log(dv.getUint8(2)); // => 1Tenter d'écrire une valeur qui nécessite plus d'espace que ce qui est disponible dans le ArrayBuffer sous-jacent provoquera une erreur. Ci-dessous, nous tentons d'écrire un Float64 (qui nécessite 8 octets) au décalage d'octet 0, mais il n'y a que quatre octets au total dans le buffer.
dv.setFloat64(0, 3.1415);
// ^ RangeError: Out of bounds accessLes méthodes suivantes sont disponibles sur DataView :
TypedArray
Les typed arrays sont une famille de classes qui fournissent une interface de type Array pour interagir avec les données dans un ArrayBuffer. Alors qu'un DataView vous permet d'écrire des nombres de taille variable à un décalage particulier, un TypedArray interprète les octets sous-jacents comme un tableau de nombres, chacun d'une taille fixe.
NOTE
Il est courant de se référer à cette famille de classes collectivement par leur superclasse partagée `TypedArray`. Cette classe est _interne_ à JavaScript ; vous ne pouvez pas directement créer des instances de celle-ci, et `TypedArray` n'est pas défini dans le scope global. Pensez-y comme une `interface` ou une classe abstraite.const buffer = new ArrayBuffer(3);
const arr = new Uint8Array(buffer);
// le contenu est initialisé à zéro
console.log(arr); // Uint8Array(3) [0, 0, 0]
// assigne des valeurs comme un tableau
arr[0] = 0;
arr[1] = 10;
arr[2] = 255;
arr[3] = 255; // no-op, hors limitesAlors qu'un ArrayBuffer est une séquence générique d'octets, ces classes de typed array interprètent les octets comme un tableau de nombres d'une taille d'octet donnée. La ligne du haut contient les octets bruts, et les lignes suivantes contiennent comment ces octets seront interprétés lorsqu'ils sont vus en utilisant différentes classes de typed array.
Les classes suivantes sont des typed arrays, avec une description de comment elles interprètent les octets dans un ArrayBuffer :
Voici le premier tableau formaté comme un tableau markdown :
| Classe | Description |
|---|---|
Uint8Array | Chaque un (1) octet est interprété comme un entier non signé 8 bits. Plage 0 à 255. |
Uint16Array | Chaque deux (2) octets sont interprétés comme un entier non signé 16 bits. Plage 0 à 65535. |
Uint32Array | Chaque quatre (4) octets sont interprétés comme un entier non signé 32 bits. Plage 0 à 4294967295. |
Int8Array | Chaque un (1) octet est interprété comme un entier signé 8 bits. Plage -128 à 127. |
Int16Array | Chaque deux (2) octets sont interprétés comme un entier signé 16 bits. Plage -32768 à 32767. |
Int32Array | Chaque quatre (4) octets sont interprétés comme un entier signé 32 bits. Plage -2147483648 à 2147483647. |
Float16Array | Chaque deux (2) octets sont interprétés comme un nombre flottant 16 bits. Plage -6.104e5 à 6.55e4. |
Float32Array | Chaque quatre (4) octets sont interprétés comme un nombre flottant 32 bits. Plage -3.4e38 à 3.4e38. |
Float64Array | Chaque huit (8) octets sont interprétés comme un nombre flottant 64 bits. Plage -1.7e308 à 1.7e308. |
BigInt64Array | Chaque huit (8) octets sont interprétés comme un BigInt signé. Plage -9223372036854775808 à 9223372036854775807 (bien que BigInt soit capable de représenter des nombres plus grands). |
BigUint64Array | Chaque huit (8) octets sont interprétés comme un BigInt non signé. Plage 0 à 18446744073709551615 (bien que BigInt soit capable de représenter des nombres plus grands). |
Uint8ClampedArray | Identique à Uint8Array, mais "plafonne" automatiquement à la plage 0-255 lors de l'assignation d'une valeur à un élément. |
Le tableau ci-dessous démontre comment les octets dans un ArrayBuffer sont interprétés lorsqu'ils sont vus en utilisant différentes classes de typed array.
| Octet 0 | Octet 1 | Octet 2 | Octet 3 | Octet 4 | Octet 5 | Octet 6 | Octet 7 | |
|---|---|---|---|---|---|---|---|---|
ArrayBuffer | 00000000 | 00000001 | 00000010 | 00000011 | 00000100 | 00000101 | 00000110 | 00000111 |
Uint8Array | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Uint16Array | 256 (1 * 256 + 0) | 770 (3 * 256 + 2) | 1284 (5 * 256 + 4) | 1798 (7 * 256 + 6) | ||||
Uint32Array | 50462976 | 117835012 | ||||||
BigUint64Array | 506097522914230528n |
Pour créer un typed array à partir d'un ArrayBuffer prédéfini :
// crée un typed array à partir d'ArrayBuffer
const buf = new ArrayBuffer(10);
const arr = new Uint8Array(buf);
arr[0] = 30;
arr[1] = 60;
// tous les éléments sont initialisés à zéro
console.log(arr); // => Uint8Array(10) [ 30, 60, 0, 0, 0, 0, 0, 0, 0, 0 ];Si nous essayions d'instancier un Uint32Array à partir de ce même ArrayBuffer, nous obtiendrions une erreur.
const buf = new ArrayBuffer(10);
const arr = new Uint32Array(buf);
// ^ RangeError: ArrayBuffer length minus the byteOffset
// is not a multiple of the element sizeUne valeur Uint32 nécessite quatre octets (16 bits). Parce que le ArrayBuffer fait 10 octets de long, il n'y a aucun moyen de diviser proprement son contenu en blocs de 4 octets.
Pour corriger cela, nous pouvons créer un typed array sur une "tranche" particulière d'un ArrayBuffer. Le Uint16Array ci-dessous ne "voit" que les premiers 8 octets du ArrayBuffer sous-jacent. Pour y parvenir, nous spécifions un byteOffset de 0 et une length de 2, ce qui indique le nombre de nombres Uint32 que notre tableau doit contenir.
// crée un typed array à partir d'une tranche d'ArrayBuffer
const buf = new ArrayBuffer(10);
const arr = new Uint32Array(buf, 0, 2);
/*
buf _ _ _ _ _ _ _ _ _ _ 10 octets
arr [_______,_______] 2 éléments de 4 octets
*/
arr.byteOffset; // 0
arr.length; // 2Vous n'avez pas besoin de créer explicitement une instance ArrayBuffer ; vous pouvez plutôt spécifier directement une longueur dans le constructeur de typed array :
const arr2 = new Uint8Array(5);
// tous les éléments sont initialisés à zéro
// => Uint8Array(5) [0, 0, 0, 0, 0]Les typed arrays peuvent également être instanciés directement à partir d'un tableau de nombres, ou d'un autre typed array :
// à partir d'un tableau de nombres
const arr1 = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
arr1[0]; // => 0;
arr1[7]; // => 7;
// à partir d'un autre typed array
const arr2 = new Uint8Array(arr);De manière générale, les typed arrays fournissent les mêmes méthodes que les tableaux réguliers, à quelques exceptions près. Par exemple, push et pop ne sont pas disponibles sur les typed arrays, car ils nécessiteraient de redimensionner le ArrayBuffer sous-jacent.
const arr = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
// prend en charge les méthodes de tableau courantes
arr.filter(n => n > 128); // Uint8Array(1) [255]
arr.map(n => n * 2); // Uint8Array(8) [0, 2, 4, 6, 8, 10, 12, 14]
arr.reduce((acc, n) => acc + n, 0); // 28
arr.forEach(n => console.log(n)); // 0 1 2 3 4 5 6 7
arr.every(n => n < 10); // true
arr.find(n => n > 5); // 6
arr.includes(5); // true
arr.indexOf(5); // 5Référez-vous à la documentation MDN pour plus d'informations sur les propriétés et méthodes des typed arrays.
Uint8Array
Il vaut la peine de mettre spécifiquement en évidence Uint8Array, car il représente un "tableau d'octets" classique — une séquence d'entiers non signés 8 bits entre 0 et 255. C'est le typed array le plus courant que vous rencontrerez en JavaScript.
Dans Bun, et un jour dans d'autres moteurs JavaScript, il dispose de méthodes disponibles pour convertir entre des tableaux d'octets et des représentations sérialisées de ces tableaux sous forme de chaînes base64 ou hex.
new Uint8Array([1, 2, 3, 4, 5]).toBase64(); // "AQIDBA=="
Uint8Array.fromBase64("AQIDBA=="); // Uint8Array(4) [1, 2, 3, 4, 5]
new Uint8Array([255, 254, 253, 252, 251]).toHex(); // "fffefdfcfb=="
Uint8Array.fromHex("fffefdfcfb"); // Uint8Array(5) [255, 254, 253, 252, 251]C'est la valeur de retour de TextEncoder#encode, et le type d'entrée de TextDecoder#decode, deux classes utilitaires conçues pour traduire des chaînes et divers encodages binaires, notamment "utf-8".
const encoder = new TextEncoder();
const bytes = encoder.encode("hello world");
// => Uint8Array(11) [ 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100 ]
const decoder = new TextDecoder();
const text = decoder.decode(bytes);
// => hello worldBuffer
Bun implémente Buffer, une API Node.js pour travailler avec des données binaires qui précède l'introduction des typed arrays dans la spécification JavaScript. Il a depuis été réimplémenté en tant que sous-classe de Uint8Array. Il fournit un large éventail de méthodes, y compris plusieurs méthodes de type Array et de type DataView.
const buf = Buffer.from("hello world");
// => Buffer(11) [ 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100 ]
buf.length; // => 11
buf[0]; // => 104, ascii pour 'h'
buf.writeUInt8(72, 0); // => ascii pour 'H'
console.log(buf.toString());
// => Hello worldPour une documentation complète, référez-vous à la documentation Node.js.
Blob
Blob est une API Web couramment utilisée pour représenter des fichiers. Blob a été initialement implémenté dans les navigateurs (contrairement à ArrayBuffer qui fait partie de JavaScript lui-même), mais il est maintenant pris en charge dans Node et Bun.
Il n'est pas courant de créer directement des instances Blob. Plus souvent, vous recevrez des instances de Blob d'une source externe (comme un élément <input type="file"> dans le navigateur) ou d'une bibliothèque. Cela dit, il est possible de créer un Blob à partir d'une ou plusieurs "parties de blob" de type string ou binaire.
const blob = new Blob(["<html>Hello</html>"], {
type: "text/html",
});
blob.type; // => text/html
blob.size; // => 19Ces parties peuvent être string, ArrayBuffer, TypedArray, DataView, ou d'autres instances Blob. Les parties de blob sont concaténées ensemble dans l'ordre où elles sont fournies.
const blob = new Blob([
"<html>",
new Blob(["<body>"]),
new Uint8Array([104, 101, 108, 108, 111]), // "hello" en binaire
"</body></html>",
]);Le contenu d'un Blob peut être lu de manière asynchrone dans divers formats.
await blob.text(); // => <html><body>hello</body></html>
await blob.bytes(); // => Uint8Array (copie le contenu)
await blob.arrayBuffer(); // => ArrayBuffer (copie le contenu)
await blob.stream(); // => ReadableStreamBunFile
BunFile est une sous-classe de Blob utilisée pour représenter un fichier chargé paresseusement sur le disque. Comme File, il ajoute une propriété name et lastModified. Contrairement à File, il ne nécessite pas que le fichier soit chargé en mémoire.
const file = Bun.file("index.txt");
// => BunFileFile
File est une sous-classe de Blob qui ajoute une propriété name et lastModified. Il est couramment utilisé dans le navigateur pour représenter des fichiers téléchargés via un élément <input type="file">. Node.js et Bun implémentent File.
// dans le navigateur !
// <input type="file" id="file" />
const files = document.getElementById("file").files;
// => File[]const file = new File(["<html>Hello</html>"], "index.html", {
type: "text/html",
});Référez-vous à la documentation MDN pour une documentation complète.
Streams
Les streams sont une abstraction importante pour travailler avec des données binaires sans tout charger en mémoire à la fois. Ils sont couramment utilisés pour lire et écrire des fichiers, envoyer et recevoir des requêtes réseau, et traiter de grandes quantités de données.
Bun implémente les API Web ReadableStream et WritableStream.
NOTE
Bun implémente également le module `node:stream`, y compris [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams), [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), et [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). Pour une documentation complète, référez-vous à la documentation Node.js.Pour créer un stream lisible simple :
const stream = new ReadableStream({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});Le contenu de ce stream peut être lu morceau par morceau avec la syntaxe for await.
for await (const chunk of stream) {
console.log(chunk);
}
// => "hello"
// => "world"Pour une discussion plus complète sur les streams dans Bun, voir API > Streams.
Conversion
Convertir d'un format binaire à un autre est une tâche courante. Cette section est destinée à servir de référence.
Depuis ArrayBuffer
Puisque ArrayBuffer stocke les données qui sous-tendent d'autres structures binaires comme TypedArray, les extraits ci-dessous ne convertissent pas depuis ArrayBuffer vers un autre format. Au lieu de cela, ils créent une nouvelle instance en utilisant les données stockées dans les données sous-jacentes.
Vers TypedArray
new Uint8Array(buf);Vers DataView
new DataView(buf);Vers Buffer
// crée Buffer sur tout l'ArrayBuffer
Buffer.from(buf);
// crée Buffer sur une tranche de l'ArrayBuffer
Buffer.from(buf, 0, 10);Vers string
En UTF-8 :
new TextDecoder().decode(buf);Vers number[]
Array.from(new Uint8Array(buf));Vers Blob
new Blob([buf], { type: "text/plain" });Vers ReadableStream
L'extrait suivant crée un ReadableStream et met en file d'attente l'intégralité du ArrayBuffer comme un seul chunk.
new ReadableStream({
start(controller) {
controller.enqueue(buf);
controller.close();
},
});Avec découpage en chunks
Pour streamer le ArrayBuffer en chunks, utilisez une vue Uint8Array et mettez chaque chunk en file d'attente.
const view = new Uint8Array(buf);
const chunkSize = 1024;
new ReadableStream({
start(controller) {
for (let i = 0; i < view.length; i += chunkSize) {
controller.enqueue(view.slice(i, i + chunkSize));
}
controller.close();
},
});Depuis TypedArray
Vers ArrayBuffer
Cela récupère le ArrayBuffer sous-jacent. Notez qu'un TypedArray peut être une vue d'une tranche du buffer sous-jacent, donc les tailles peuvent différer.
arr.buffer;Vers DataView
Pour créer un DataView sur la même plage d'octets que le TypedArray.
new DataView(arr.buffer, arr.byteOffset, arr.byteLength);Vers Buffer
Buffer.from(arr);Vers string
En UTF-8 :
new TextDecoder().decode(arr);Vers number[]
Array.from(arr);Vers Blob
// seulement si arr est une vue de son entier TypedArray de support
new Blob([arr.buffer], { type: "text/plain" });Vers ReadableStream
new ReadableStream({
start(controller) {
controller.enqueue(arr);
controller.close();
},
});Avec découpage en chunks
Pour streamer le ArrayBuffer en chunks, divisez le TypedArray en chunks et mettez chaque chunk en file d'attente individuellement.
new ReadableStream({
start(controller) {
for (let i = 0; i < arr.length; i += chunkSize) {
controller.enqueue(arr.slice(i, i + chunkSize));
}
controller.close();
},
});Depuis DataView
Vers ArrayBuffer
view.buffer;Vers TypedArray
Fonctionne seulement si le byteLength du DataView est un multiple du BYTES_PER_ELEMENT de la sous-classe TypedArray.
new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
new Uint16Array(view.buffer, view.byteOffset, view.byteLength / 2);
new Uint32Array(view.buffer, view.byteOffset, view.byteLength / 4);
// etc...Vers Buffer
Buffer.from(view.buffer, view.byteOffset, view.byteLength);Vers string
En UTF-8 :
new TextDecoder().decode(view);Vers number[]
Array.from(view);Vers Blob
new Blob([view.buffer], { type: "text/plain" });Vers ReadableStream
new ReadableStream({
start(controller) {
controller.enqueue(view.buffer);
controller.close();
},
});Avec découpage en chunks
Pour streamer le ArrayBuffer en chunks, divisez le DataView en chunks et mettez chaque chunk en file d'attente individuellement.
new ReadableStream({
start(controller) {
for (let i = 0; i < view.byteLength; i += chunkSize) {
controller.enqueue(view.buffer.slice(i, i + chunkSize));
}
controller.close();
},
});Depuis Buffer
Vers ArrayBuffer
buf.buffer;Vers TypedArray
new Uint8Array(buf);Vers DataView
new DataView(buf.buffer, buf.byteOffset, buf.byteLength);Vers string
En UTF-8 :
buf.toString();En base64 :
buf.toString("base64");En hex :
buf.toString("hex");Vers number[]
Array.from(buf);Vers Blob
new Blob([buf], { type: "text/plain" });Vers ReadableStream
new ReadableStream({
start(controller) {
controller.enqueue(buf);
controller.close();
},
});Avec découpage en chunks
Pour streamer le ArrayBuffer en chunks, divisez le Buffer en chunks et mettez chaque chunk en file d'attente individuellement.
new ReadableStream({
start(controller) {
for (let i = 0; i < buf.length; i += chunkSize) {
controller.enqueue(buf.slice(i, i + chunkSize));
}
controller.close();
},
});Depuis Blob
Vers ArrayBuffer
La classe Blob fournit une méthode de commodité à cet effet.
await blob.arrayBuffer();Vers TypedArray
await blob.bytes();Vers DataView
new DataView(await blob.arrayBuffer());Vers Buffer
Buffer.from(await blob.arrayBuffer());Vers string
En UTF-8 :
await blob.text();Vers number[]
Array.from(await blob.bytes());Vers ReadableStream
blob.stream();Depuis ReadableStream
Il est courant d'utiliser Response comme représentation intermédiaire pratique pour faciliter la conversion de ReadableStream vers d'autres formats.
stream; // ReadableStream
const buffer = new Response(stream).arrayBuffer();Cependant, cette approche est verbeuse et ajoute une surcharge qui ralentit inutilement les performances globales. Bun implémente un ensemble de fonctions de commodité optimisées pour convertir ReadableStream vers divers formats binaires.
Vers ArrayBuffer
// avec Response
new Response(stream).arrayBuffer();
// avec fonction Bun
Bun.readableStreamToArrayBuffer(stream);Vers Uint8Array
// avec Response
new Response(stream).bytes();
// avec fonction Bun
Bun.readableStreamToBytes(stream);Vers TypedArray
// avec Response
const buf = await new Response(stream).arrayBuffer();
new Int8Array(buf);
// avec fonction Bun
new Int8Array(Bun.readableStreamToArrayBuffer(stream));Vers DataView
// avec Response
const buf = await new Response(stream).arrayBuffer();
new DataView(buf);
// avec fonction Bun
new DataView(Bun.readableStreamToArrayBuffer(stream));Vers Buffer
// avec Response
const buf = await new Response(stream).arrayBuffer();
Buffer.from(buf);
// avec fonction Bun
Buffer.from(Bun.readableStreamToArrayBuffer(stream));Vers string
En UTF-8 :
// avec Response
await new Response(stream).text();
// avec fonction Bun
await Bun.readableStreamToText(stream);Vers number[]
// avec Response
const arr = await new Response(stream).bytes();
Array.from(arr);
// avec fonction Bun
Array.from(new Uint8Array(Bun.readableStreamToArrayBuffer(stream)));Bun fournit un utilitaire pour résoudre un ReadableStream vers un tableau de ses chunks. Chaque chunk peut être une string, un typed array, ou un ArrayBuffer.
// avec fonction Bun
Bun.readableStreamToArray(stream);Vers Blob
new Response(stream).blob();Vers ReadableStream
Pour diviser un ReadableStream en deux streams qui peuvent être consommés indépendamment :
const [a, b] = stream.tee();