Converting arraybuffer to string : Maximum call stack size exceeded - javascript

This is my code.
var xhr = new XMLHttpRequest();
xhr.open('GET',window.location.href, true);
xhr.responseType = "arraybuffer";
xhr.onload = function(event) {
debugger;
console.log(" coverting array buffer to string ");
alert(String.fromCharCode.apply(null, new Uint8Array(this.response)));
};
xhr.send();
That request is being made to a PDF URL which is around 3 MB in size. I have read a few threads with same error, Maximum call stack size exceeded, telling me that there must be some recursive call but I do not see any recursive call here. Can anyone help?

I had the same problem and I finally used this code:
function _arrayBufferToBase64( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}

The error is caused by a limitation in the number of function arguments. See "RangeError: Maximum call stack size exceeded" Why?
Instead of String.fromCharCode.apply(), use e. g. a TextEncoder. See Uint8Array to string in Javascript

this.response.arrayBuffer()
.then((buf) => {
const uint8Array = new Uint8Array(buf);
const data = uint8Array.reduce((acc, i) => acc += String.fromCharCode.apply(null, [i]), '');
return data;
})

Thank you so much Anthony O, you saved my 10 days work.
small modification in my scenario is:
Angular 7 :
/** Convert Uint8Array to string */
private static arrayBufferToBase64( buffer: Uint8Array ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return binary;
}

Related

How do you convert from ArrayBuffer to byte array in javascript? [duplicate]

I want to read a binary file in JavaScript that would be gotten through XMLHttpRequest and be able to manipulate that data. From my researching I discovered this method of reading a binary file data into an array
var xhr = new XMLHttpRequest();
xhr.open('GET', '/binary_And_Ascii_File.obj', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
var uInt8Array = new Uint8Array(this.response);
};
How do I convert this binary data array to a human-readable-string?
I'm sure you will find this helpful: http://jsdo.it/tsmallfield/uint8array.
Click on javascript tab.
There will appear the code to convert the Uint8Array in a string. The author shows 2 method:
The first is about creating a view.
The second offsetting bytes.
EDIT: report the code for completeness
var buffer = new ArrayBuffer( res.length ), // res is this.response in your case
view = new Uint8Array( buffer ),
len = view.length,
fromCharCode = String.fromCharCode,
i, s, str;
/**
* 1) 8bitの配列に入れて上位ビットけずる
*/
str = "";
for ( i = len; i--; ) {
view[i] = res[i].charCodeAt(0);
}
for ( i = 0; i < len; ++i ) {
str += fromCharCode( view[i] );
}
/**
* 2) & 0xff で上位ビットけずる
*/
str = "";
for ( i = 0; i < len; ++i ) {
str += fromCharCode( res[i].charCodeAt(0) & 0xff );
}
function load_binary_resource(url) {
var byteArray = [];
var req = new XMLHttpRequest();
req.open('GET', url, false);
req.overrideMimeType('text\/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) return byteArray;
for (var i = 0; i < req.responseText.length; ++i) {
byteArray.push(req.responseText.charCodeAt(i) & 0xff)
}
return byteArray;
}
See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data for more details

Convert base64 string into byte array through javascript

I want to convert "base64" string into bytes array through the javascript.
I received string from the URL like.
("data:image/jpeg;base64,/9j/4QN6RXhpZgAASUkqAAgAAAAIA)
And i want to convert the string into bytes array. because i need this byte array to send Rest API endpoint. The rest API Content-type=application/octet-stream.
You can use XMLHttpRequest to do the dirty work for you:
var url = document.createElement("canvas").toDataURL(); // some data-uri
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "arraybuffer";
xhr.onload = function() {
// result = ArrayBuffer, from here assign a view to it
if (xhr.status === 200) console.log(new Uint8Array(xhr.response));
};
xhr.send();
It's a little more code than using atob() but all conversion happens internally. It's async too which can help with larger Data-URIs.
In newer browsers (which supports it) you can instead use fetch():
// note: atm not all browsers support these features.
fetch(document.createElement("canvas").toDataURL()) // pass in some data-uri
.then(function(response) {return response.arrayBuffer()})
.then(function(buffer) {
console.log(new Uint8Array(buffer));
});
You can try with the following;
function test(base64StringFromURL)
{
var parts = base64StringFromURL.split(";base64,");
var contentType = parts[0].replace("data:", "");
var base64 = parts[1];
var byteArray = base64ToByteArray(base64);
..
}
function base64ToByteArray(base64String) {
try {
var sliceSize = 1024;
var byteCharacters = atob(base64String);
var bytesLength = byteCharacters.length;
var slicesCount = Math.ceil(bytesLength / sliceSize);
var byteArrays = new Array(slicesCount);
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
var begin = sliceIndex * sliceSize;
var end = Math.min(begin + sliceSize, bytesLength);
var bytes = new Array(end - begin);
for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return byteArrays;
} catch (e) {
console.log("Couldn't convert to byte array: " + e);
return undefined;
}
}
This would prove to be much short solution.
const byteArray = new Buffer(base64String.replace(/^[\w\d;:\/]+base64\,/g, ''), 'base64');
base64String is the string containing the base 64 string.
byteArray is the array you need.
The regex replacement is optional and is just there to deal with prefix as in the case of dataurl string.

Convert from byte array to StringBase64

I have an array with bytes and need them to be in StringBase64 and I am using the following:
var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayDigestion)));
console.log('digestionB64_2 .......:' + digestionB64);
with the following result:
digestionB64_2 .......:6d9310a8df39348ef2bbd8a0f04f65bba64180666848526a4c93e86aa69433e7
And I've also used the following code and get the same results no difference whatsoever:
function arrayBufferToString(buffer) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return binary;
}
As I understand Strings in Base 64 must end with an = or == characters
Would it be valid if I add those characters manually at the end or is it valid to have a StringBase64 without them?

Edit and add bytes to binary(uint8array)

I want to add a string and some binaries like 0x00 inside my binary file which I loaded via ajax (to get it as binary)
This is my code until now:
var ajax = new XMLHttpRequest();
ajax.open("GET", "test.bin", true);
ajax.responseType = "arraybuffer";
ajax.onload = function () {
var byteArray = new Uint8Array(ajax.response);
// What can i do?
};
ajax.send();
Well, at last I could resolve it:
var str = 'Injected!!!';
var injAr = [0x21,0xFE,str.length];
for (var i = 0; i < str.length; i++) { injAr.push(str.charCodeAt(i));}
injAr.push(0x00,0x3B);
var ajax = new XMLHttpRequest();
ajax.open("GET", "test.gif", true);
ajax.responseType = "arraybuffer";
ajax.onload = function () {
var bAr = new Uint8Array(ajax.response), len = bAr.length - 1;
window.newBAr = new Uint8Array(len + injAr.length);
for (var i = 0; i < len; i++) { newBAr[i] = bAr[i];}
for (var i = 0; i < injAr.length; i++) { newBAr[len + i] = injAr[i];}
}
ajax.send();
Well, this was intended to add comments blocks to a gif correctly, but it can be use also with jpgs, pngs, for example to modify or add exif metadata
You will need to have the reqponse in a format that the javascript will be able to pull out the bytes that are the string and seperate the bytes that are part of the binary. You can do this by positioning the string at the beginning or end with a distinctive byte flag to signify the boundary. I would put the string at the beginning with the flag at the end of it to signify the beginning of the binary since I am unsure of the binary file.
Once you have it seperated, you will need to convert the bytes to numbers and then convert the numbers to characters. Here is a question that goes over how to convert binary to characters.

ArrayBuffer to base64 encoded string

I need an efficient (read native) way to convert an ArrayBuffer to a base64 string which needs to be used on a multipart post.
function _arrayBufferToBase64( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
but, non-native implementations are faster e.g. https://gist.github.com/958841
see http://jsperf.com/encoding-xhr-image-data/6
Updated benchmarks: https://jsben.ch/wnaZC
This works fine for me:
var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
In ES6, the syntax is a little simpler:
const base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
As pointed out in the comments, this method may result in a runtime error in some browsers when the ArrayBuffer is large. The exact size limit is implementation dependent in any case.
For those who like it short, here's an other one using Array.reduce which will not cause stack overflow:
var base64 = btoa(
new Uint8Array(arrayBuffer)
.reduce((data, byte) => data + String.fromCharCode(byte), '')
);
There is another asynchronous way use Blob and FileReader.
I didn't test the performance. But it is a different way of thinking.
function arrayBufferToBase64( buffer, callback ) {
var blob = new Blob([buffer],{type:'application/octet-binary'});
var reader = new FileReader();
reader.onload = function(evt){
var dataurl = evt.target.result;
callback(dataurl.substr(dataurl.indexOf(',')+1));
};
reader.readAsDataURL(blob);
}
//example:
var buf = new Uint8Array([11,22,33]);
arrayBufferToBase64(buf, console.log.bind(console)); //"CxYh"
const blob = new Blob([array]);
const reader = new FileReader();
reader.onload = (event) => {
const dataUrl = event.target.result;
const [_, base64] = dataUrl.split(',');
// do something with base64
};
reader.readAsDataURL(blob);
Or as a promisified utility:
async function encode(array) {
return new Promise((resolve) => {
const blob = new Blob([array]);
const reader = new FileReader();
reader.onload = (event) => {
const dataUrl = event.target.result;
const [_, base64] = dataUrl.split(',');
resolve(base64);
};
reader.readAsDataURL(blob);
});
}
const encoded = await encode(typedArray);
The OP did not specify the Running Enviroment but if you are using Node.JS there is a very simple way to do such thing.
Accordig with the official Node.JS docs
https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings
// This step is only necessary if you don't already have a Buffer Object
const buffer = Buffer.from(yourArrayBuffer);
const base64String = buffer.toString('base64');
Also, If you are running under Angular for example, the Buffer Class will also be made available in a Browser Environment.
My recommendation for this is to NOT use native btoa strategies—as they don't correctly encode all ArrayBuffer's…
rewrite the DOMs atob() and btoa()
Since DOMStrings are 16-bit-encoded strings, in most browsers calling window.btoa on a Unicode string will cause a Character Out Of Range exception if a character exceeds the range of a 8-bit ASCII-encoded character.
While I have never encountered this exact error, I have found that many of the ArrayBuffer's I have tried to encode have encoded incorrectly.
I would either use MDN recommendation or gist.
https://github.com/beatgammit/base64-js
https://gist.github.com/jonleighton/958841
I used this and works for me.
function arrayBufferToBase64( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
function base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array( len );
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
Below are 2 simple functions for converting Uint8Array to Base64 String and back again
arrayToBase64String(a) {
return btoa(String.fromCharCode(...a));
}
base64StringToArray(s) {
let asciiString = atob(s);
return new Uint8Array([...asciiString].map(char => char.charCodeAt(0)));
}
If you're okay with adding a library, base64-arraybuffer:
yarn add base64-arraybuffer
then:
encode(buffer) - Encodes ArrayBuffer into base64 string
decode(str) - Decodes base64 string to ArrayBuffer
This worked for me:
Buffer.from(myArrayBuffer).toString("base64");
ABtoB64(ab) {
return new Promise(res => {
const fr = new FileReader();
fr.onload = ({target: {result: s}}) => res(s.slice(s.indexOf(';base64,') + 8));
fr.readAsDataURL(new Blob([ab]));
});
}
asynchronous method using file reader.
You can derive a normal array from the ArrayBuffer by using Array.prototype.slice.
Use a function like Array.prototype.map to convert bytes in to characters and join them together to forma string.
function arrayBufferToBase64(ab){
var dView = new Uint8Array(ab); //Get a byte view
var arr = Array.prototype.slice.call(dView); //Create a normal array
var arr1 = arr.map(function(item){
return String.fromCharCode(item); //Convert
});
return window.btoa(arr1.join('')); //Form a string
}
This method is faster since there are no string concatenations running in it.
In the Browser suggested solutions with btoa seem fine.
But in Node.js btoa is deprecated
It is recommended to use buffer.toString(encoding)
like
const myString = buffer.toString("base64")
i use TextDecode api to convert it to normal text and then convert it to Base64
const uint = new Uint8Array([ 73, 32, 108, 111, 118, 101, 32, 121, 111, 117 ]).buffer
const decoder = new TextDecoder()
const decodedText = decoder.decode(uint)
const base64Code = btoa(decodedText)
Use uint8-to-b64 package to do encoding/decoding in browser and Node.js
By my side, using Chrome navigator, I had to use DataView() to read an arrayBuffer
function _arrayBufferToBase64( tabU8A ) {
var binary = '';
let lecteur_de_donnees = new DataView(tabU8A);
var len = lecteur_de_donnees.byteLength;
var chaine = '';
var pos1;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( lecteur_de_donnees.getUint8( i ) );
}
chaine = window.btoa( binary )
return chaine;}
function _arrayBufferToBase64(uarr) {
var strings = [], chunksize = 0xffff;
var len = uarr.length;
for (var i = 0; i * chunksize < len; i++){
strings.push(String.fromCharCode.apply(null, uarr.subarray(i * chunksize, (i + 1) * chunksize)));
}
return strings.join("");
}
This is better, if you use JSZip for unpack archive from string

Categories

Resources