Cómo pasar de Blob a ArrayBuffer

110

Estaba estudiando Blobs y noté que cuando tienes un ArrayBuffer, puedes convertirlo fácilmente en un Blob de la siguiente manera:

var dataView = new DataView(arrayBuffer);
var blob = new Blob([dataView], { type: mimeString });

La pregunta que tengo ahora es, ¿es posible pasar de un Blob a un ArrayBuffer?

Jeanluca Scaljeri
fuente
2
Los blobs no están en formato JS nativo. Son como referencias a los datos, pero no a los datos reales. Los datos de a Blobno se pueden leer directamente, pero se pueden hacer con algunas API.
tripulso

Respuestas:

39

La ResponseAPI consume un (inmutable) Blobdel cual los datos se pueden recuperar de varias formas. El OP solo pidió ArrayBuffer, y aquí hay una demostración de ello.

var blob = GetABlobSomehow();

// NOTE: you will need to wrap this up in a async block first.
/* Use the await keyword to wait for the Promise to resolve */
await new Response(blob).arrayBuffer();   //=> <ArrayBuffer>

alternativamente, puede usar esto:

new Response(blob).arrayBuffer()
.then(/* <function> */);

Nota: esta API no es compatible con los navegadores más antiguos ( antiguos ), así que consulte la Tabla de compatibilidad de navegadores para estar seguro;)

tripulso
fuente
1
Esta debería ser la respuesta principal en mi opinión.
Cameron Martin
const arrayBuffer = espera nueva respuesta (blob) .arrayBuffer ();
R.Cha
1
@ R.Cha Gracias por la solución. ¡No me di cuenta de eso!
tripulso
2
Truco inteligente y que no debe confundirse con el Blob.arrayBuffer()que en realidad tiene una compatibilidad bastante pobre incluso en 2020, caniuse.com/#feat=mdn-api_blob_arraybuffer o developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBuffer
jpschroeder
¿Cuál es el rendimiento de esto? ¿Copia los datos en el Blob o simplemente devuelve una vista?
GaryO
141

Puede usar FileReaderpara leer el Blobcomo un ArrayBuffer.

He aquí un breve ejemplo:

var arrayBuffer;
var fileReader = new FileReader();
fileReader.onload = function(event) {
    arrayBuffer = event.target.result;
};
fileReader.readAsArrayBuffer(blob);

Aquí tienes un ejemplo más extenso:

// ArrayBuffer -> Blob
var uint8Array  = new Uint8Array([1, 2, 3]);
var arrayBuffer = uint8Array.buffer;
var blob        = new Blob([arrayBuffer]);

// Blob -> ArrayBuffer
var uint8ArrayNew  = null;
var arrayBufferNew = null;
var fileReader     = new FileReader();
fileReader.onload  = function(event) {
    arrayBufferNew = event.target.result;
    uint8ArrayNew  = new Uint8Array(arrayBufferNew);

    // warn if read values are not the same as the original values
    // arrayEqual from: http://stackoverflow.com/questions/3115982/how-to-check-javascript-array-equals
    function arrayEqual(a, b) { return !(a<b || b<a); };
    if (arrayBufferNew.byteLength !== arrayBuffer.byteLength) // should be 3
        console.warn("ArrayBuffer byteLength does not match");
    if (arrayEqual(uint8ArrayNew, uint8Array) !== true) // should be [1,2,3]
        console.warn("Uint8Array does not match");
};
fileReader.readAsArrayBuffer(blob);
fileReader.result; // also accessible this way once the blob has been read

Esto se probó en la consola de Chrome 27—69, Firefox 20—60 y Safari 6—11.

Aquí también hay una demostración en vivo con la que puedes jugar: https://jsfiddle.net/potatosalad/FbaM6/

Actualización 2018-06-23: Gracias a Klaus Klein por el consejo sobre event.target.resultversusthis.result

Referencia:

ensalada de papas
fuente
22
¿No parece esto mucho código ... para algo que debería ser simple?
Henley Chiu
2
@HenleyChiu Edité la respuesta para incluir una versión corta del código. El ejemplo más largo está destinado a ser completamente autónomo (muestra cómo crear ArrayBuffer, Blob y viceversa). No he podido encontrar una forma síncrona de leer un Blob sin usar un Web Worker y FileReaderSync .
Ensalada de patatas
1
Algunas personas realmente quieren demostrar que existe el infierno de devolución de llamada. Tiene sentido para BLOB GRANDES, pero para casos de uso normales, JavaScript debería proporcionar un método de sincronización.
lama12345
esto fue muy útil
Jimmy Obonyo Abor
@Anuj Has hecho mal. No debería referirse a this. <EventTarget>.resultdebería arreglar eso!
tripulse
17

Solo para complementar la respuesta del Sr. @potatosalad.

En realidad, no necesita acceder al alcance de la función para obtener el resultado en la devolución de llamada onload , puede hacer lo siguiente libremente en el parámetro del evento :

var arrayBuffer;
var fileReader = new FileReader();
fileReader.onload = function(event) {
    arrayBuffer = event.target.result;
};
fileReader.readAsArrayBuffer(blob);

¿Por qué esto es mejor? Porque entonces podemos usar la función de flecha sin perder el contexto

var fileReader = new FileReader();
fileReader.onload = (event) => {
    this.externalScopeVariable = event.target.result;
};
fileReader.readAsArrayBuffer(blob);
Klaus Klein
fuente
15

O puede utilizar la API de recuperación

fetch(URL.createObjectURL(myBlob)).then(res => res.arrayBuffer())

No sé cuál es la diferencia de rendimiento, y esto también se mostrará en la pestaña de su red en DevTools.

Arlen Beiler
fuente
17
O simplementenew Response(blob).arrayBuffer()
Endless
4

Hay ahora un (Chrome 76+ y 69+ FF) Blob.prototype.arrayBuffer () método que devolverá una promesa con una resolución de ArrayBuffer que representa los datos del blob.

(async () => {
  const blob = new Blob(['hello']);
  const buf = await blob.arrayBuffer();
  console.log( buf.byteLength ); // 5
})();

Kaiido
fuente
2
Desafortunadamente, actualmente no funciona en Safari (todavía)
HerrZatacke