How to go from Blob to ArrayBuffer
Asked Answered
S

7

145

I was studying Blobs, and I noticed that when you have an ArrayBuffer, you can easily convert this to a Blob as follows:

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

The question I have now is, is it possible to go from a Blob to an ArrayBuffer?

Stilly answered 11/3, 2013 at 15:5 Comment(1)
Blobs aren't in native JS format. They're like references to data but not the actual data. Data from a Blob cannot be directly read but it could be done with some APIs.Xeroderma
X
75

The Response API consumes a (immutable) Blob from which the data can be retrieved in several ways. The OP only asked for ArrayBuffer, and here's a demonstration of it.

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>

alternatively you could use this:

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

Note: This API isn't compatible with older (ancient) browsers so take a look to the Browser Compatibility Table to be on the safe side ;)

Xeroderma answered 17/3, 2019 at 6:45 Comment(5)
Clever trick and not to be confused with Blob.arrayBuffer() which actually has quite poor compatibility even in 2020, caniuse.com/#feat=mdn-api_blob_arraybuffer or developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBufferInvest
What's the performance of this? Does it copy the data in the Blob or just return a view of it?Liberec
@Liberec Haven't benchmarked it, I assume it copies the blob and returns a view over it.Xeroderma
Sigh, it appears it does copy. That's bad if the blob is big!Liberec
FYI await blob.arrayBuffer() has good support now: developer.mozilla.org/en-US/docs/Web/API/Blob/…Auburn
B
162

You can use FileReader to read the Blob as an ArrayBuffer.

Here's a short example:

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

Here's a longer example:

// 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: https://mcmap.net/q/37117/-how-to-check-if-two-arrays-are-equal-with-javascript-duplicate
    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

This was tested out in the console of Chrome 27—69, Firefox 20—60, and Safari 6—11.

Here's also a live demonstration which you can play with: https://jsfiddle.net/potatosalad/FbaM6/

Update 2018-06-23: Thanks to Klaus Klein for the tip about event.target.result versus this.result

Reference:

Bowra answered 12/4, 2013 at 21:19 Comment(4)
doesn't this seem like a lot of code.. for something that should be simple?Jacintojack
@HenleyChiu I edited the answer to include a short version of the code. The longer example is intended to be fully self-contained (shows how to create the ArrayBuffer, the Blob, and back again). I haven't been able to find a synchronous way to read a Blob without using a Web Worker and FileReaderSync.Bowra
Some people really want to prove the callback hell exists. It makes sense for LARGE blobs, but for normal use cases JavaScript should provide a sync method.Ma
FYI await blob.arrayBuffer() has good support now: developer.mozilla.org/en-US/docs/Web/API/Blob/…Auburn
X
75

The Response API consumes a (immutable) Blob from which the data can be retrieved in several ways. The OP only asked for ArrayBuffer, and here's a demonstration of it.

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>

alternatively you could use this:

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

Note: This API isn't compatible with older (ancient) browsers so take a look to the Browser Compatibility Table to be on the safe side ;)

Xeroderma answered 17/3, 2019 at 6:45 Comment(5)
Clever trick and not to be confused with Blob.arrayBuffer() which actually has quite poor compatibility even in 2020, caniuse.com/#feat=mdn-api_blob_arraybuffer or developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBufferInvest
What's the performance of this? Does it copy the data in the Blob or just return a view of it?Liberec
@Liberec Haven't benchmarked it, I assume it copies the blob and returns a view over it.Xeroderma
Sigh, it appears it does copy. That's bad if the blob is big!Liberec
FYI await blob.arrayBuffer() has good support now: developer.mozilla.org/en-US/docs/Web/API/Blob/…Auburn
S
22

Or you can use the fetch API

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

I don't know what the performance difference is, and this will show up on your network tab in DevTools as well.

Shonda answered 9/8, 2018 at 3:0 Comment(2)
Or just new Response(blob).arrayBuffer()Balaton
Or await blob.arrayBuffer() which now has good support: developer.mozilla.org/en-US/docs/Web/API/Blob/…Auburn
D
18

Just to complement Mr @potatosalad answer.

You don't actually need to access the function scope to get the result on the onload callback, you can freely do the following on the event parameter:

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

Why this is better? Because then we may use arrow function without losing the context

var fileReader = new FileReader();
fileReader.onload = (event) => {
    this.externalScopeVariable = event.target.result;
};
fileReader.readAsArrayBuffer(blob);
Dossier answered 2/10, 2017 at 13:3 Comment(0)
N
12

There is now (Chrome 76+ & FF 69+) a Blob.prototype.arrayBuffer() method which will return a Promise resolving with an ArrayBuffer representing the Blob's data.

(async () => {
  const blob = new Blob(['hello']);
  const buf = await blob.arrayBuffer();
  console.log( buf.byteLength ); // 5
})();
Nestorius answered 11/3, 2013 at 15:5 Comment(3)
Unfortunately currently does not work in Safari (yet)Files
@HerrZatacke, see my answerLira
According to developer.mozilla.org/en-US/docs/Web/API/Blob/… Safari supports this API with version 14+.Auburn
V
12

This is an async method which first checks for the availability of arrayBuffer method. This function is backward compatible and future proof.

async function blobToArrayBuffer(blob) {
    if ('arrayBuffer' in blob) return await blob.arrayBuffer();
    
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result);
        reader.onerror = () => reject();
        reader.readAsArrayBuffer(blob);
    });
}
Vitriolic answered 12/2, 2021 at 7:32 Comment(2)
It seems to be a wrong answer since the return value of reader.onerror cannot be a function. so it should be like the following. reader.onerror = (e) => reject(e) or reader.onerror = rejectFleet
I had to move readAsArrayBuffer before the onload/onerror declarations for my unit tests to run.Gradualism
L
10

await blob.arrayBuffer() is good.

The problem is when iOS / Safari support is needed.. for that  one would need this:

Blob.prototype.arrayBuffer ??=function(){ return new Response(this).arrayBuffer() }
Lira answered 24/8, 2020 at 20:28 Comment(4)
https://mcmap.net/q/158511/-how-to-go-from-blob-to-arraybuffer and all the other fallbacks are already there.Nestorius
@Kaiido, See #15342412. My method is superior and is required until iOS support. (after which the line can simply be removed)Lira
And a FileReader is still needed in IE. My point is that your answer doesn't offer anything more than what the previous answers gave. If one wants to build a polyfill, all the various ways of doing the same task are already there.Nestorius
@Kaiido, IE requirements are rare. ¶ Yep, this is the only answer with the polyfill.Lira

© 2022 - 2024 — McMap. All rights reserved.