16

Our app downloads a zip file, but the response is in binary.

So what I did is to convert it to base64. It works when the size is 87.7KB but an error occurs when the response size is 183KB.

The error is Uncaught RangeError: Maximum call stack size exceeded

The line in question is

btoa(String.fromCharCode.apply(null, new Uint8Array(blob)))

According to this answer, the String.fromCharCode.apply() must be replaced with TextEncoder.

So I changed it to

btoa(new TextDecoder('utf-8').decode(new Uint8Array(blob)))

but I get an error.

Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

I changed it again using the top most snippet of this answer

The new code is now

btoa(unescape(encodeURIComponent(new TextDecoder('utf-8').decode(new Uint8Array(blob)))))

The download now works but the download zip file is corrupted.

The whole code can be seen here

Dev
  • 1,592
  • 2
  • 22
  • 45
  • I already posted a comment on your previous answer, but this is an X-Y issue, you really don't need to go through b64, not even through AB. – Kaiido Mar 06 '18 at 06:49

2 Answers2

43

I got my answer from another question

btoa(new Uint8Array(blob).reduce(function (data, byte) {
    return data + String.fromCharCode(byte);
}, ''));

Source

Dev
  • 1,592
  • 2
  • 22
  • 45
  • this code gets rid of maximum stack size error, but i am using this converted string to generate url which i can use in my embed element as src. But it gives me net::ERR_INVALID_URL error. This only happens with the pdf files which were giving stack size exceeded error. Small pdf file works just fine. Please help. – Harshit Agarwal May 09 '19 at 04:25
-2

https://stackoverflow.com/a/40077712/6582356

function blobToB64(data) {
    if ('TextDecoder' in window) {
      // Decode as UTF-8
      var dataView = new DataView(data);
      var decoder = new TextDecoder('utf8');
      return btoa(decoder.decode(dataView));
    } else {
      // Fallback
      return btoa(new Uint8Array(data).reduce((data, byte) =>
        data + String.fromCharCode(byte),
        ''))
    }
}

https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder

This one is seems to have better performance

  • TextDecoder example is incorrect, since it decodes UTF-8: `blobToB64(new Uint8Array([240, 159, 152, 128]).buffer)` *Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.* [240, 159, 152, 128] is code for smiley emoji. – dchest Aug 02 '19 at 12:15
  • @dchest agreed, using something like https://www.npmjs.com/package/js-base64 instead of btoa would fix this – Benito Gómez Aug 02 '19 at 15:41