24

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 settig 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.

Brian Tompsett - 汤莱恩
  • 5,195
  • 62
  • 50
  • 120
Tono Nam
  • 29,637
  • 73
  • 252
  • 421
  • See this post: http://stackoverflow.com/questions/7370943/retrieving-binary-file-content-using-javascript-base64-encode-it-and-reverse-de – Constantine Dec 06 '12 at 14:06

5 Answers5

20

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

var base64EncodedStr = btoa(unescape(encodeURIComponent(rawData)));
Frane Poljak
  • 2,137
  • 19
  • 21
  • 1
    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. – eyalewin Dec 10 '17 at 16:22
  • 5
    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. – Frane Poljak Dec 18 '17 at 13:41
  • 2
    Use `decodeURI` instead of `unescape` which is depreciated: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/unescape – Justin Feb 14 '20 at 12:06
  • 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." – chunk_split Mar 26 '21 at 20:08
  • Since this question isn't specifically about images, I've addressed my solution here (which relies on `String.fromCharCode` to fix `btoa()` errors) : https://stackoverflow.com/a/66823987/899075 – chunk_split Mar 26 '21 at 20:37
7

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]));
});
Bastian Voigt
  • 4,617
  • 5
  • 34
  • 61
  • 1
    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.response` – Bouke Versteegh Dec 29 '17 at 19:26
  • 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. – Asu Nov 14 '20 at 00:38
6

Try the btoa function:

   var data = btoa(r);
Esailija
  • 130,716
  • 22
  • 250
  • 308
  • 60
    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." – Bastian Voigt Mar 12 '15 at 09:16
  • @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. – Esailija Mar 12 '15 at 12:10
  • 5
    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? – Bastian Voigt Mar 12 '15 at 14:30
  • 1
    @BastianVoigt because you have normal unicode string and not a binary string – Esailija Mar 12 '15 at 18:39
  • 1
    @Esailija 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 :( – Wowzaaa Nov 05 '15 at 11:39
  • 3
    It seems that image files often contain unicode strings inside alongside with the binary data. So this answer *may not work for images*. – Marcos Sandrini Nov 15 '18 at 09:47
3

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;
}
Barun
  • 1,747
  • 3
  • 25
  • 46
0
//dataArr is a Uint8Array        
function toBase64(dataArr){
            return btoa(dataArr.reduce((data, val)=> {
                 return data + String.fromCharCode(val);
            }, ''));
        }
ch_g
  • 427
  • 2
  • 5