1

I've been pulling my hair out for hours now with a question that has been asked a few times around here: I have binary data from a flash audio recorder, which I turn into a dataURL and then want to convert to a blob for upload. SO gave me several answers, which are all essentially the same:

I wrote the following code based on the above

var data = window.atob(dataURL.split(',')[1]);
var length = data.length;
var uInt8Array = new Uint8Array(length);
for (var i = 0; i < length; ++i) {
    uInt8Array[i] = data.charCodeAt(i);
}
var myfile = new Blob([uInt8Array], {'type': 'audio/x-wav'});

This works perfectly in firefox nightly (20) and chrome 25, but chromium 20 keeps telling me the size of myfile is 19 bytes (when it should be several kb), and subsequent upload fails consistently. Is this a bug in chromium, or did I miss a doc saying it is not supported yet? Any alternative ideas?

Community
  • 1
  • 1
Laurent S
  • 4,106
  • 3
  • 26
  • 50
  • 1
    Although [this answer](http://stackoverflow.com/a/11954337/1558890) says the `BlobBuilder` API is deprecated, have you tried if using it works on Chromium? The example code in that answer populates the untyped array, and then converts it to a typed array. I can't see why it would matter, but have you tried this? A jsfiddle could be useful... – Jim Garrison Dec 12 '12 at 17:23
  • 1
    well, it looks like `BlobBuilder/webkitBlobBuilder` is not even there anymore in chromium, so I can't even fallback on it... – Laurent S Dec 12 '12 at 23:15
  • 1
    oh, and yes, I did try the untyped array first, no luck. – Laurent S Dec 12 '12 at 23:17

2 Answers2

1

It looks like there's a bug in chromium's Blob() constructor when called with certain parameters. I narrowed down the issue on jsfiddle (run in chromium to see the issue, other browsers seem to handle it properly, both blob sizes should be 32)

Here's the code I put together to get around it:

var data = window.atob(dataURL.split(',')[1]);
var length = data.length;
var buffer = new ArrayBuffer(length);   // added this line
var uInt8Array = new Uint8Array(buffer);
for (var i = 0; i < length; ++i) {
    uInt8Array[i] = data.charCodeAt(i);
}
// two options here:
// buggy: produces a 19byte long file in chromium
var myfile = new Blob([uInt8Array], {'type': 'audio/x-wav'});
// works in chromium (and firefox), but produces a deprecation warning in chrome
var myfile = new Blob([buffer], {'type': 'audio/x-wav'});

I ended up running the first alternative, then if (myfile.size == 19) { to get the second one if needed

Laurent S
  • 4,106
  • 3
  • 26
  • 50
  • See https://code.google.com/p/chromium/issues/detail?id=165907 for the filed bug against chromium. – Jim Garrison Dec 13 '12 at 16:53
  • Also, the code above never populates `buffer`. Do you do that in the loop when using the second method? – Jim Garrison Dec 13 '12 at 16:54
  • 1
    `Uint8Array` is a view on the buffer, so filling it also populates the `ArrayBuffer`. See https://developer.mozilla.org/en-US/docs/JavaScript_typed_arrays#Buffers_and_views.3A_typed_array_architecture for details – Laurent S Dec 13 '12 at 18:44
1

Use my code convert dataURI to blob.

function dataURLtoBlob(dataURL) {
    var arr = dataURL.split(','), mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
    while(n--){
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], {type:mime});
}
cuixiping
  • 24,167
  • 8
  • 82
  • 93