I need an efficient (read native) way to convert an ArrayBuffer to a base64 string which needs to be used on a multipart post.
21 Answers
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
jsPerf.com is jsPerf.app now: https://jsperf.app/encoding-xhr-image-data/51
Updated benchmarks: https://jsben.ch/wnaZC
15 Comments
join()ing them at the end is significantly faster on Firefox, IE, and Safari (but quite a lot slower on Chrome): jsperf.com/tobase64-implementationstoString('base64') method.Buffer doesn't exist in the browser.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.
11 Comments
btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))btoa is safe for characters in the code range 0-255, as this is here the case (Think about the 8 in Uint8Array).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), '')
);
5 Comments
<amount of Bytes in the buffer> new strings.btoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))?btoa(Array.from(new Uint8Array(arraybuffer)).map(b => String.fromCharCode(b)).join('')).fromCharCode(byte & 0xff) works.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.
3 Comments
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.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"
5 Comments
dataurl.split(',', 2)[1]instead of dataurl.substr(dataurl.indexOf(',')+1).readAsDataURL could theoretically return a percent encoded dataURI (And it seems it is actually the case in jsdom)split be better than substring?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);
7 Comments
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;
}
2 Comments
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.
4 Comments
btoa not works on String, but OP is asking ArrayBuffer.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)));
}
4 Comments
function keyword and it should work in a modern browser.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
1 Comment
Native method Uint8Array.prototype.toBase64 is now available!
Uint8Array.prototype.toBase64 has been implemented on most major browsers (see caniuse) and can be used as follows:
const arr = new Uint8Array([ 73, 32, 108, 111, 118, 101, 32, 121, 111, 117 ]);
const str = arr.toBase64();
console.log(str); // SSBsb3ZlIHlvdQ==It's worth noting that since .toBase64 is a natively implemented function, it's incredibly fast (in fact, much faster than any of the top-voted answers).
You can also use this core-js polyfill:
const arr = new Uint8Array([ 73, 32, 108, 111, 118, 101, 32, 121, 111, 117 ]);
const base64 = arr.toBase64();
console.log(base64);<script src="https://unpkg.com/[email protected]/minified.js"></script>Comments
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)
3 Comments
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).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)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")
Comments
Here is a ES6 solution that is 3 times faster than using the native btoa function and is 1.25 faster than the non-native solution proposed by mobz and Emmanuel. Its code also respect more recent good coding practices and uses way more clean binary masks for readability :
    /**
     * @param {ArrayBuffer} buffer
     * @return {string}
     */
    function toBase64(buffer) {
        const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        const byteLength = buffer.byteLength;
        const bufferView = new Uint8Array(buffer);
        const remainingBytesCount = byteLength % 3;
        const mainLength = byteLength - remainingBytesCount;
        let string = "";
        let i = 0;
        for (; i < mainLength; i += 3) {
            const chunk = (bufferView[i] << 16) | (bufferView[i + 1] << 8) | bufferView[i + 2];
            string += base64Chars[(chunk & 0b111111000000000000000000) >> 18];
            string += base64Chars[(chunk & 0b000000111111000000000000) >> 12];
            string += base64Chars[(chunk & 0b000000000000111111000000) >> 6];
            string += base64Chars[(chunk & 0b000000000000000000111111)];
        }
        if (remainingBytesCount === 2) {
            const chunk = (bufferView[i] << 16) | (bufferView[i + 1] << 8);
            string += base64Chars[(chunk & 0b111111000000000000000000) >> 18];
            string += base64Chars[(chunk & 0b000000111111000000000000) >> 12];
            string += base64Chars[(chunk & 0b000000000000111111000000) >> 6];
            string += "=";
        } else if (remainingBytesCount === 1) {
            const chunk = (bufferView[i] << 16);
            string += base64Chars[(chunk & 0b111111000000000000000000) >> 18];
            string += base64Chars[(chunk & 0b000000111111000000000000) >> 12];
            string += "==";
        }
        return string;
    }
3 Comments
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.
1 Comment
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;}
Comments
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






















FormDataand aBlobfrom ArrayBuffer, append the blob to formdata and post binary data instead of base64.