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
join()
ing them at the end is significantly faster on Firefox, IE, and Safari (but quite a lot slower on Chrome): jsperf.com/tobase64-implementations –
Faliscan Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
–
Decrease String.fromCharCode()
behave different for byte values >= 0x80. –
Conspicuous toString('base64')
method. –
Saturnian Buffer
doesn't exist in the browser. –
Manifestation 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.
btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
–
Scag btoa
is safe for characters in the code range 0-255, as this is here the case (Think about the 8 in Uint8Array
). –
Catwalk ...
is much slower than using apply
. –
Lemmuela 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), '')
);
<amount of Bytes in the buffer>
new strings. –
Foliation btoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))
? –
Peccadillo btoa(Array.from(new Uint8Array(arraybuffer)).map(b => String.fromCharCode(b)).join(''))
. –
Robey The OP did not specify the Running Environment, but if you are using Node.JS there is a very simple way to do this.
According to 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.
Javascript
. So I updated my answer to make it more concise. I think this is an important answer because I was searching how to do this and could not get to the best answer to the problem. –
Saturnian 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"
dataurl.split(',', 2)[1]
instead of dataurl.substr(dataurl.indexOf(',')+1)
. –
Ettore readAsDataURL
could theoretically return a percent encoded dataURI (And it seems it is actually the case in jsdom) –
Brundisium split
be better than substring
? –
Brundisium This example uses the built-in FileReader readDataURL() to do the conversion to base64 encoding. Data URLs are structured data:[<mediatype>][;base64],<data>
, so we split that url at the comma and return only the base64 encoded characters.
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);
data:*/*;base64,
) to obtain just the Base64 string. See MDN docs –
Centralization 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;
}
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.
btoa
not works on String, but OP is asking ArrayBuffer
. –
Gilbertine 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)));
}
function
keyword and it should work in a modern browser. –
Aquamarine This worked for me:
Buffer.from(myArrayBuffer).toString("base64");
from
method? –
Mayenne If you're okay with adding a library, base64-arraybuffer:
yarn add base64-arraybuffer
then:
encode(buffer)
- Encodes ArrayBuffer into base64 stringdecode(str)
- Decodes base64 string to ArrayBuffer
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.
In the Browser suggested solutions with btoa
seem fine.
But in Node.js btoa is Legacy
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)
btoa(new TextDecoder().decode(myUint8Array))
Don't know why this isn't rated higher; it's just like the answer that uses String.fromCharCode(...myUint8Array)
, except without the stack-overflow concern (afaik). –
Dagmardagna TextDecoder
works for characters 0-127 (those in the Latin1
set), but errors for array values between 128-255. So the other answer does have wider compatibility, if you think you may encounter array-values outside the 0-127 (Latin1
) range. (actually, this one is maybe better since avoids the stack-overflow issue that the other can hit; or perhaps this one, though has negative of being async) –
Dagmardagna 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.
var uint8Array = new Uint8Array(BytesArray);
var base64Str = btoa(String.fromCharCode(...uint8Array));
Or,
var base64Str = btoa(uint8Array.reduce((x, y) => x + String.fromCharCode(y), ''));
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
© 2022 - 2024 — McMap. All rights reserved.
FormData
and aBlob
from ArrayBuffer, append the blob to formdata and post binary data instead of base64. – Transmissible