Convert binary data to base64 with JavaScript
Asked Answered
Z

10

32

HTML5 enable you to store data locally which I think it is great. For example here is how you can use it:

var store = window.localStorage;
store.setItem('foo', "hellow world");
var test = store.getItem('foo');
// test should = "hellow world"

In HTML you can dynamically display an image by setting its source to:

"data:image/jpg;base64," + (base64string)

So my question is how can I convert binary data to a base64 string so that I can take advantage of html5 local storage?

For example it will be great if I could:

$.ajax({
   url: 'someImage.png',
   type: 'POST',
   success: function (r) {
                
      // here I want to convert r to a base64 string !
      // r is not binary so maybe I have to use a different approach
      var data = ConvertToBase64(r);
      document.getElementById("img").src = "data:image/png;base64," + data;

   },
});

I know I could solve this problem by wrapping the image around a canvas using html5 then converting that to base64string. also I can make a specific service on the server that will send a base64 string data of that image (someImage.aspx).I just want to know if it will by possible to retrieve binary data from a server and convert that to a base64 string.

Zymase answered 11/6, 2012 at 15:3 Comment(1)
See this post: #7371443Oasis
L
4

Try the btoa function:

   var data = btoa(r);
Labiodental answered 11/6, 2012 at 15:4 Comment(8)
Unfortunatly this does not work for binary data: Uncaught InvalidCharacterError: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range."Heartwhole
@BastianVoigt binary strings don't contain charaters outside Latin1, every byte is represented by a code point between 0 and 255, which is the Latin1 range.Labiodental
My Chrome does not agree. Or why would it present me this error message? Is it because my string is interpreted as UTF-8 somehow?Heartwhole
@BastianVoigt because you have normal unicode string and not a binary stringLabiodental
@Labiodental and how do you use the normal unicode string? i mean i am trying to save it to a jpeg but with very little luck :(Tinhorn
It seems that image files often contain unicode strings inside alongside with the binary data. So this answer may not work for images.Garment
btoa doesn't work with binary data. btoa([ -318009162, 2021411928, 745061637, 961782484 ]) returns LTMxODAwOTE2MiwyMDIxNDExOTI4LDc0NTA2MTYzNyw5NjE3ODI0ODQ= when it should be returning tpAL7VhMfHgFvWgs1KJTOQ==, btoa literally converts the object representation to a string, then encodes that. That's why other users are getting inconsistent results...Navarrette
btoa does work with binary data, but it depends on how you define "binary data". btoa works with single-byte strings. You can convert an unicode string to an ASCII string by converting the unicode string to an Uint8Array and from there back to a string to create a correctly encoded output. Same goes for any kind of binary data, like files (JPEG images in your example). Convert them to Uint8Array first and then to an ASCII string (e. g. with String.fromCodePoint()), which is basically what we can call a "binary string". See my answer below.Mitchellmitchem
E
23

To prevent 'InvalidCharacterError' error, you need to do this:

var base64EncodedStr = btoa(unescape(encodeURIComponent(rawData)));
Exterminatory answered 3/1, 2017 at 13:51 Comment(5)
The deprecated unescape() method computes a new string in which hexadecimal escape sequences are replaced with the character that it represents. The escape sequences might be introduced by a function like escape. Because unescape() is deprecated, use decodeURI() or decodeURIComponent instead.Acariasis
Using either decodeURI and decodeURIComponent causes "Error: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range", so I'm sticking with unsecape.Exterminatory
Use decodeURI instead of unescape which is depreciated: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Risible
Issue in my case is that unescape for some reason results in an invalid image (which doesn't render) and moving to decodeURI is probably the right move, since this brings me right back to the issue with btoa(), which results in the inevitable error: "Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range."Topology
Since this question isn't specifically about images, I've addressed my solution here (which relies on String.fromCharCode to fix btoa() errors) : https://mcmap.net/q/454786/-how-can-i-convert-image-binary-from-api-call-to-data-uri-in-javascriptTopology
H
11

Use a FileReader to encode your image as a data URL:

jQuery.ajax({...})
.done(function (r) {
  var reader = new FileReader(
  reader.onload = (function(self) {
    return function(e) {
      document.getElementById("img").src = e.target.result;
    }
  })(this);
  reader.readAsDataURL(new Blob([r]));
});
Heartwhole answered 12/3, 2015 at 10:5 Comment(2)
works great! i'm using this in combination with req = new XMLHttpRequest(), i had to set req.responseType = 'blob' to make it work. The blob data is in req.responseResiniferous
Just to add, the contents of the string from e.target.result will start with the file type, e.g. data:application/pdf;base64, then followed by actual base64 string. So, if you need the pure base64, split by comma and take the second part.Ideograph
V
7

This is old question, but could not find a better answer, so I wrote down this function. It will convert the Uint8Array directly to Base64 without converting it into a string before base64. Hope it will help someone.

var encoder = new TextEncoder("ascii");
var decoder = new TextDecoder("ascii");
var base64Table = encoder.encode('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=');
function toBase64(dataArr){
    var padding = dataArr.byteLength % 3;
    var len = dataArr.byteLength - padding;
    padding = padding > 0 ? (3 - padding) : 0;
    var outputLen = ((len/3) * 4) + (padding > 0 ? 4 : 0);
    var output = new Uint8Array(outputLen);
    var outputCtr = 0;
    for(var i=0; i<len; i+=3){              
        var buffer = ((dataArr[i] & 0xFF) << 16) | ((dataArr[i+1] & 0xFF) << 8) | (dataArr[i+2] & 0xFF);
        output[outputCtr++] = base64Table[buffer >> 18];
        output[outputCtr++] = base64Table[(buffer >> 12) & 0x3F];
        output[outputCtr++] = base64Table[(buffer >> 6) & 0x3F];
        output[outputCtr++] = base64Table[buffer & 0x3F];
    }
    if (padding == 1) {
        var buffer = ((dataArr[len] & 0xFF) << 8) | (dataArr[len+1] & 0xFF);
        output[outputCtr++] = base64Table[buffer >> 10];
        output[outputCtr++] = base64Table[(buffer >> 4) & 0x3F];
        output[outputCtr++] = base64Table[(buffer << 2) & 0x3F];
        output[outputCtr++] = base64Table[64];
    } else if (padding == 2) {
        var buffer = dataArr[len] & 0xFF;
        output[outputCtr++] = base64Table[buffer >> 2];
        output[outputCtr++] = base64Table[(buffer << 4) & 0x3F];
        output[outputCtr++] = base64Table[64];
        output[outputCtr++] = base64Table[64];
    }
    
    var ret = decoder.decode(output);
    output = null;
    dataArr = null;
    return ret;
}
Voucher answered 21/8, 2020 at 16:40 Comment(0)
S
5
//dataArr is a Uint8Array        
function toBase64(dataArr){
            return btoa(dataArr.reduce((data, val)=> {
                 return data + String.fromCharCode(val);
            }, ''));
        }
Succentor answered 6/5, 2021 at 20:43 Comment(1)
This seems to be a good answer, short and concise, It handles my problem with... Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 rangeTrainer
L
4

Try the btoa function:

   var data = btoa(r);
Labiodental answered 11/6, 2012 at 15:4 Comment(8)
Unfortunatly this does not work for binary data: Uncaught InvalidCharacterError: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range."Heartwhole
@BastianVoigt binary strings don't contain charaters outside Latin1, every byte is represented by a code point between 0 and 255, which is the Latin1 range.Labiodental
My Chrome does not agree. Or why would it present me this error message? Is it because my string is interpreted as UTF-8 somehow?Heartwhole
@BastianVoigt because you have normal unicode string and not a binary stringLabiodental
@Labiodental and how do you use the normal unicode string? i mean i am trying to save it to a jpeg but with very little luck :(Tinhorn
It seems that image files often contain unicode strings inside alongside with the binary data. So this answer may not work for images.Garment
btoa doesn't work with binary data. btoa([ -318009162, 2021411928, 745061637, 961782484 ]) returns LTMxODAwOTE2MiwyMDIxNDExOTI4LDc0NTA2MTYzNyw5NjE3ODI0ODQ= when it should be returning tpAL7VhMfHgFvWgs1KJTOQ==, btoa literally converts the object representation to a string, then encodes that. That's why other users are getting inconsistent results...Navarrette
btoa does work with binary data, but it depends on how you define "binary data". btoa works with single-byte strings. You can convert an unicode string to an ASCII string by converting the unicode string to an Uint8Array and from there back to a string to create a correctly encoded output. Same goes for any kind of binary data, like files (JPEG images in your example). Convert them to Uint8Array first and then to an ASCII string (e. g. with String.fromCodePoint()), which is basically what we can call a "binary string". See my answer below.Mitchellmitchem
B
1

Fair and simply solution for modern day browsers

fetch('https://picsum.photos/536/354', {
    method: 'GET',
})
.then((data) => {
    // IMP to convert your json or other response to blob ( for this you have to check your api response is file / binary 
    return data.blob()
})
.then((data) => {
    var reader = new FileReader();
    reader.onload = function() {                         
        var b64 = reader.result
        console.log("This is base64", b64)
        document.getElementById("imagetoShow").src = b64
    }
    reader.readAsDataURL(data)
})
.catch((error) => {
    error.text().then( errorMessage => {
        console.log(errorMessage)
    })
})
<image src="" width="200" height="200" id="imagetoShow"/>
Boogie answered 29/6, 2021 at 3:13 Comment(1)
,,,which was shared six years prior here....Arian
A
1

Just fyi for code using node.js, it's just two simple function calls. I've wrapped them in a function for convenience.

export function convertToBase64(bytes) {
  return bytes.toString("base64");
}

export function convertFromBase64(str) {
  return Buffer.from(str, "base64");
}
Antipathetic answered 8/7, 2022 at 19:56 Comment(0)
M
0

JavaScript doesn't really has a concept of "binary data" or "binary strings". The closest it gets to binary data are specific array types. JavaScript offers the two functions btoa() and atob() for Base64 conversion, however these two functions work with single-bytes strings only (ASCII), which means that they won't work with, for example, unicode characters which (can, like UTF-8) use multiple bytes per character.

To circumvent this problem, you can convert your data to the Uint8Array array type. For texts you can use the TextEncoder class, which is supported by all modern browsers.

var strArray = new TextEncoder().encode("Hellö world 😎");

Since btoa works with ASCII strings and not arrays, you have to convert the Uint8Array to ASCII:

var strAscii = String.fromCodePoint(...strArray);

And then encode it with btoa to Base64 safely:

var strBase64 = btoa(strAscii);

For more information, see the "The Unicode Problem" section in the MDN article for Base64. There is the same example with a function to decode the Base64 string. The Base64 data is then generally compatible with other software (like PHP).

Mitchellmitchem answered 27/10, 2023 at 10:33 Comment(0)
P
0

Luckily, it seems that binary Base64 en/decoding will be added to the standard eventually:

It's not yet implemented in latest stable Firefox or Chrome though.

Periodical answered 1/6, 2024 at 15:39 Comment(0)
W
0

In case if you are trying to insert a downloaded image into an img element it would be easier to get the data as blob and then convert to base64.

Check this answer if this is what you are trying to do.

Download an image using Axios and convert it to base64

Whitewood answered 5/6, 2024 at 8:15 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.