How can you encode a string to Base64 in JavaScript?
Asked Answered
K

33

1236

I have a PHP script that can encode a PNG image to a Base64 string.

I'd like to do the same thing using JavaScript. I know how to open files, but I'm not sure how to do the encoding. I'm not used to working with binary data.

Kingbolt answered 29/10, 2008 at 13:34 Comment(4)
Here is best way to base64_encode and base64_decode using javascript. See below links. phpjs.org/functions/base64_encode:358 phpjs.org/functions/base64_decode:357Presentiment
here is another jquery plugin fo base64 encode/decodeInesita
Check microjs: microjs.com/#base64Stinkwood
Referenced in meta question Basically identical answers - Only difference: correction of errors.Debor
T
1360

You can use btoa() and atob() to convert to and from base64 encoding.

There appears to be some confusion in the comments regarding what these functions accept/return, so…

  • btoa() accepts a “string” where each character represents an 8-bit byte – if you pass a string containing characters that can’t be represented in 8 bits, it will probably break. This isn’t a problem if you’re actually treating the string as a byte array, but if you’re trying to do something else then you’ll have to encode it first.

  • atob() returns a “string” where each character represents an 8-bit byte – that is, its value will be between 0 and 0xff. This does not mean it’s ASCII – presumably if you’re using this function at all, you expect to be working with binary data and not text.

See also:


Most comments here are outdated. You can probably use both btoa() and atob(), unless you support really outdated browsers.

Check here:

Thursby answered 29/10, 2008 at 15:31 Comment(23)
Note that this also works for webkit browsers, such as Safari.Juniorjuniority
Please note the special consideration for Unicode strings: developer.mozilla.org/En/DOM/Window.btoa#Unicode_Strings btoa and atob only work properly for ASCII based strings. As an American, you probably won't notice a difference ... but the first time you use an accented character, your code will break.Melburn
If these functions support only ascii strings, then they should not be used at all. Period. You base 64 encode byte arrays, not character arrays (i.e. strings). Strings are logical character arrays, not byte arrays. Character arrays need to first be transformed into byte arrays by choosing a character encoding (UTF8,UTF16, or UTF32) to transform each character into a sequences of one or more bytes (for variable-length encoding schemes such as UTF8 or UFT16) or sequences of a fixed length (in constant-length encoding schemes such as UTF32). Once you've got a byte array, then base64 encode it.Leflore
See my edit, @Triynko. These are not intended to be used to process text, period.Thursby
Ok, I was thrown off by Dan's comment about btoa and atob only working properly for ASCII strings. These function are, in fact, not meant to work with actual character strings at all, but rather with byte arrays that happen to be stored in strings, such that for each character in the string, calling charCodeAt(i) will always return a value between 0 and 255. I've seen this before in a Base64 JavaScript class that would first utf8 encode the string into another string (exactly as described where each "character" actually represents a byte value) and then base 64 encodes that.Leflore
So an even more efficient method would be to use something like this (webtoolkit.info/javascript-base64.html) and just encode via: var encoded = btoa(Base64._utf8_encode(input)) and decode using var decoded = Base64._utf8_decode(atob(encoded)). It seems to me that performing a utf8 encoding is much less error prone than passing the input through unescape(encodeURIComponent(str)). Also, the string = string.replace(/\r\n/g,"\n"); line should be removed from the _utf8_encode function, since such a line break normalization has no business being in a basic utf8 encoding function.Leflore
Here's a polyfill github.com/davidchambers/Base64.js , it's best to use the native functions and polyfill rather than include a library that introduces a new API.Skean
And the reverse to base64-decode a UTF8-string as encoded by the method of SET: decodeURIComponent(escape(window.atob(b64)));Reaction
To whoever suggested this edit: really cannot emphasize enough that you should not be passing arbitrary strings to btoa(). Yes, that example would work... But it's misleading; wide characters (legal in a string literal) would cause problems.Thursby
To better remember the names of the functions: btoa() is before to after, atob() is after to beforeTimer
Javascript is such a drunk language. atob appears to stand for "alphanumeric to base64" so of course it actually does the opposite.Fronniah
binary, @Will. The b stands for "binary". Like those sketchy newsgroups.Thursby
yeahbutstill... It's used to convert strings to base64... any non-drunk coder would have called it toBase64, would have supported unicode, and would have then gone out to drink.Fronniah
So, that's where folks keep getting confused here, @Will - it absolutely should not be used to convert strings to or from anything, unless they're strings of Base64 (a.k.a, alt.binaries -> "b"). As you can see, the coders weren't drunk, just pervy.Thursby
It's pronounced b to a and a to b, b standing for binary and a standing for ASCIINunnally
The "a" input/output should be valid ASCII, as that is the usual intent of Base64 @Captain. Do not under any circumstances assume that the "b" output is valid in any text encoding, nor send any text that doesn't fit into 8-bits into a function expecting "b".Thursby
@Thursby As jpp suggested, I'd like to bring my meta question and corresponding edit of Sunny Milenov's answer to your attention. I hope that contacting you this way is acceptable.Addi
I'll try to find some time to look at that, @TS - but keep in mind, a lot of the answers here are doing extra stuff because, unlike the original asker, they're trying to pass Unicode strings through systems that don't support Unicode... And using a Base64 encoding as part of that process. While this is certainly understandable, it does make for some conflicting (and... ill-defined) requirements at times.Thursby
Not sure why this is an accepted or popular answer - it does nothing to address the Base64 specification by the OP. Instead, you're telling the OP to not use Base64 at all. There are other better answers which directly address the Base64 stipulation of the OP's question.Pitiable
Sure it does, @tpartee: the input/output of atob/btoa is base64. That's what the 'a' is in these abbreviations. That bit's easy enough, and I don't think too many people get tripped up anymore - what seems to throw them is the other side: the 'b'. Since originally JavaScript didn't have byte arrays, these functions are defined such that binary input must be contained in strings - but, and I keep having to emphasize this - they should NOT be passed arbitrary strings. That's not what they're for, or what they expect.Thursby
This function is only provided for compatibility with legacy web platform APIs and should never be used in new code, because they use strings to represent binary data and predate the introduction of typed arrays in JavaScript. For code running using Node.js APIs, converting between base64-encoded strings and binary data should be performed using Buffer.from(str, 'base64') andbuf.toString('base64').Jackfish
@YuriAps, your comment doesn't add any value unfortunately, you are referring to a browser side function and saying they shouldn't be used, then your offered replacement is a server side function that doesn't exist in browsers!Cannady
Re: the above two comments... NodeJS supports these as of v16. No I don't know why it took so long, but if you're after cross-platform functions for handling base64, these two functions are good choices.Thursby
O
325

From here:

/**
*
*  Base64 encode / decode
*  http://www.webtoolkit.info/
*
**/
var Base64 = {

    // private property
    _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

    // public method for encoding
    encode : function (input) {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;

        input = Base64._utf8_encode(input);

        while (i < input.length) {

            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output = output +
            this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
            this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
        }
        return output;
    },

    // public method for decoding
    decode : function (input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {

            enc1 = this._keyStr.indexOf(input.charAt(i++));
            enc2 = this._keyStr.indexOf(input.charAt(i++));
            enc3 = this._keyStr.indexOf(input.charAt(i++));
            enc4 = this._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }
        }

        output = Base64._utf8_decode(output);

        return output;
    },

    // private method for UTF-8 encoding
    _utf8_encode : function (string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    },

    // private method for UTF-8 decoding
    _utf8_decode : function (utftext) {
        var string = "";
        var i = 0;
        var c = c1 = c2 = 0;

        while ( i < utftext.length ) {

            c = utftext.charCodeAt(i);

            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else {
                c2 = utftext.charCodeAt(i+1);
                c3 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    }
}

Also, search for "JavaScript base64 encoding" turns up a lot of other options, and the above was the first one.

Oder answered 29/10, 2008 at 13:39 Comment(15)
This is also useful when the base64 encoding is non-standard; in my case the "/" character wasn't used, and the "?" character was used instead, meaning even in Chrome atob() wasn't going to decode the base64 strings that were incoming.Chromoplast
Be careful with this code - it attempts to interpret your string as a UTF-8 encoded string. We had a case where we had a binary string (i.e. each character in the string should be interpreted as a byte), and this code did corrupt the data. Read the source, Luke.Cordelier
If you use code from webtoolkito info do not forget about copyright: /** * * Base64 encode / decode * webtoolkit.info * **/Sturges
All that is needed to make it safe for most binary encoding/decodings it to remove the questionable string = string.replace(/\r\n/g,"\n"); statement in the utf8 encoding method.Fachanan
I would use the above method with small amount of data. For bigger files is better to consider multi-part POST, where the file is transferred as binary.Oder
@Marius: I'm wondering why they would they even include string = string.replace(/\r\n/g,"\n"); in the first place, lol. It's like "oh, lets encode this string, but first, why don't we just randomly normalize all the line breaks for no good reason at all". That should absolutely be removed from the class under all circumstances.Leflore
I know Base64 was intended for other uses, and has a 33% overhead. Can this be optimized for web? So when you have an image with 100 Bytes, it gets saved in string form as 100 bytes instead of about 133 bytes?Germain
@Leflore when people blindly implement solutions from here without checking it a little bit, then they probably get what they deserve. (not really, I don't mind helping people, but I can only hope they at least read these comments!)Fachanan
I'm not a javascript guru, but this code seems to contain a bug: if chr2 is NaN, its value is still used in the statement enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);. In my browser this works out ok, NaN>>4 equals 0, but I don't know if all browsers do this (also, NaN/16 equals NaN).Bradstreet
@Triynko: Today I discovered that I'd been affected by that line that clobbered "\r\n" into "\n". It certainly took me a while to suspect the base 64 encoding function of mangling my strings!Hayes
input.charCodeAt(i++) always returns 65535 for characters from 128(10000000)-255(11111111) in IE9!Valera
Forcing an entire community to use linear complexity instead of constant (in decode) is a pretty unorthodox thing ;)Leibniz
add .toString() to replace in _utf8_encode function because if json string is passed it will trigger exceptionMauromaurois
Doesn't work on unicode. Do: Base64.encode("😉") and then decode it here: base64decode.orgLeda
The _utf8_encode / _utf8_decode methods implemented above can handle 2 byte and 3 byte UTF-8 sequences, but NOT 4 byte ones. It will fail to correctly encode any code point > 0xFFFF. For example 𝒜 which is code point 0x1D49C in Unicode, would have the 4 byte UTF sequence f0 9d 92 9c. Which this will not generate.Quietism
H
277

Internet Explorer 10+

// Define the string
var string = 'Hello World!';

// Encode the String
var encodedString = btoa(string);
console.log(encodedString); // Outputs: "SGVsbG8gV29ybGQh"

// Decode the String
var decodedString = atob(encodedString);
console.log(decodedString); // Outputs: "Hello World!"

Cross-Browser

// Create Base64 Object
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}}

// Define the string
var string = 'Hello World!';

// Encode the String
var encodedString = Base64.encode(string);
console.log(encodedString); // Outputs: "SGVsbG8gV29ybGQh"

// Decode the String
var decodedString = Base64.decode(encodedString);
console.log(decodedString); // Outputs: "Hello World!"

jsFiddle


With Node.js

In Node.js you can encode normal text to base64 with Buffer.fromString

/* 
  Buffer() requires a number, array or string as the first parameter, 
  and an optional encoding type as the second parameter.
  The default is "utf8". Possible encoding types are 
  "ascii", "utf8", "ucs2", "base64", "binary", and "hex"
*/
var b = Buffer.from('JavaScript');
/*
  If we don't use toString(), JavaScript assumes we want to convert the 
  object to utf8.
  We can make it convert to other formats by passing the encoding 
  type to toString().
*/

var s = b.toString('base64');

And here is how you decode base64 encoded strings:

var b = Buffer.from('SmF2YVNjcmlwdA==', 'base64')
var s = b.toString();

With Dojo.js

To encode an array of bytes using dojox.encoding.base64:

var str = dojox.encoding.base64.encode(myByteArray);

To decode a Base64-encoded string:

var bytes = dojox.encoding.base64.decode(str)

Bower install angular-base64

<script src="bower_components/angular-base64/angular-base64.js"></script>

angular
    .module('myApp', ['base64'])
    .controller('myController', [

    '$base64', '$scope',
    function($base64, $scope) {

        $scope.encoded = $base64.encode('a string');
        $scope.decoded = $base64.decode('YSBzdHJpbmc=');
}]);
Handball answered 22/10, 2014 at 18:6 Comment(4)
This answer is based on the original code and DOES NOT include updates to that code posted in other answers here.Bandurria
Proposed NodeJS solution is deprecated.Koeninger
I converted @davidcondrey's cross-browser Base64 object to be compatible with TypeScript. Available here: Github GistSynod
on Node.js, for some reason, it's faster to encode with Buffer.from("x").toString("base64") than your own implementation (cross-browser), but it's much faster to decode with your own implementation (cross-browser).Alphonsa
R
100

Sunny's code is great except it breaks in Internet Explorer 7 because of references to "this". It was fixed by replacing such references with "Base64":

var Base64 = {
    // private property
    _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

    // public method for encoding
    encode : function (input) {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;

        input = Base64._utf8_encode(input);

        while (i < input.length) {

            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output = output +
            Base64._keyStr.charAt(enc1) + Base64._keyStr.charAt(enc2) +
            Base64._keyStr.charAt(enc3) + Base64._keyStr.charAt(enc4);
        }

        return output;
    },

    // public method for decoding
    decode : function (input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {

            enc1 = Base64._keyStr.indexOf(input.charAt(i++));
            enc2 = Base64._keyStr.indexOf(input.charAt(i++));
            enc3 = Base64._keyStr.indexOf(input.charAt(i++));
            enc4 = Base64._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }
        }

        output = Base64._utf8_decode(output);

        return output;
    },

    // private method for UTF-8 encoding
    _utf8_encode : function (string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    },

    // private method for UTF-8 decoding
    _utf8_decode : function (utftext) {
        var string = "";
        var i = 0;
        var c = c1 = c2 = 0;

        while ( i < utftext.length ) {

            c = utftext.charCodeAt(i);

            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else {
                c2 = utftext.charCodeAt(i+1);
                c3 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    }
}
Robins answered 18/7, 2011 at 22:8 Comment(5)
ooh my bad, I was taking the input from browser URL; where | is converted to %7C; hence the encoding also wrong.Sexennial
I know this is really old, but I have seen this function used in more than one place, the key string is actually at 65 characters, not 64. The string is not standard spec, I am not sure it matters, but was just wondering if it does?Befit
"use strict"; is what breaks the 'this' and other type elements like 'with' and from what I have read, 'eval' gets a bashing. All misplaced ideas on abuse. Personally I do not see why JavaScript needs to go down the route its going, it was never meant to be a program tightly bound and made more complex than it is already. If you want to be bound then make a compiler for javascript.Counterpane
I try to use this function and I receive the error: Caused by: org.mozilla.javascript.EcmaError: TypeError: Cannot find function replace in object teste teste teste I'm trying to encode .txt with "teste teste teste". Anyone knows why this error?Hexagram
@JonathanWagner - there are 64 characters used for the normal encoding. The 65th character is used as padding them the input string does not have a number of characters divisible by 3.Radiometer
F
95

You can use btoa (to Base64) and atob (from Base64).

For Internet Explorer 9 and below, try the jquery-base64 plugin:

$.base64.encode("this is a test");
$.base64.decode("dGhpcyBpcyBhIHRlc3Q=");
Fugleman answered 27/12, 2011 at 3:27 Comment(8)
Why does everything need to be a jQuery plugin :c this is just core JavaScript functionality this has nothing to do with the DOM or jQueryDissatisfy
This is not a core functionality or there wouldn't be as many different high voted answers (including do-it-yourself tl;dr code). So, imho this is actually a good use case for jQuery (one liner, expected to work even in Android's WebView) - even more if it's already a dependency.Polenta
I've gone and given up. A jQuery subset should be formalized and introduced into the JS standard like a ".net". It's not pretty, but the need is overwhelming and it's come to rule the ecosystem.Laird
I like to install code snippets like this into jQuery mainly because they'll exist in a controlled namespace. If you're not using AMD or CommonJS or a similar design pattern, it's easy for your global namespace to get really messy with a bunch of random functions.Starkey
w3schools.com/jsref/met_win_btoa.asp indicates btoa support for chrome, ie10+, firefox, safari, operaErnestineernesto
This "Global Namespace" business, and using frameworks for execution of small programs, bag idea and JQuery is written in JavaScript, IT IS NOT JavaScript. Read up about atob() and btoa() because you will find that it is not consistent across browser types.Counterpane
@Polenta - except its functionality does not depend on or extend anything jQuery at all...literally the only references to jQuery in its code are attaching it to the jQuery object...so what's the point in attaching it to jQuery and therefore requiring jQuery to use? Just make it it's own 1 liner base64.encode(...) and base64.decode(...) ...attaching it to jQuery when it has zero jQuery specific functionality makes absolutely no sense...Attack
jQuery was not requested. Not a valid answer to a plain old JS question.Seldan
E
94

From the comments (by SET and Stefan Steiger) below the accepted answer, here is a quick summary of how to encode/decode a string to/from Base64 without need of a library.

str = "The quick brown fox jumps over the lazy dog";
b64 = btoa(unescape(encodeURIComponent(str)));
str = decodeURIComponent(escape(window.atob(b64)));

Pure JavaScript Demo

const input = document.getElementsByTagName('input')[0];
const btnConv = document.getElementById('btnConv');
const btnDeConv = document.getElementById('btnDeConv');

input.value = "The quick brown fox jumps over the lazy dog";

btnConv.addEventListener('click', () => {
  const txt = input.value;
  const b64 = btoa(unescape(encodeURIComponent(txt)));
  input.value = b64;
  btnDeConv.style.display = 'block';
  btnConv.style.display = 'none';
});

btnDeConv.addEventListener('click', () => {
  var b64 = input.value;
  var txt = decodeURIComponent(escape(window.atob(b64)));
  input.value = txt;
  btnConv.style.display = 'block';
  btnDeConv.style.display = 'none';
});
input{width:500px;}
#btnDeConv{display:none;}
<div><input type="text" /></div>
<button id="btnConv">Convert</button>
<button id="btnDeConv">DeConvert</button>

.

jQuery Demo (uses the jQuery library for display, but not for encode/decode)

str = "The quick brown fox jumps over the lazy dog";

$('input').val(str);

$('#btnConv').click(function(){
  var txt = $('input').val();
  var b64 = btoa(unescape(encodeURIComponent(txt)));
  $('input').val(b64);
  $('#btnDeConv').show();
});
$('#btnDeConv').click(function(){
  var b64 = $('input').val();
  var txt = decodeURIComponent(escape(window.atob(b64)));
  $('input').val(txt);
});
#btnDeConv{display:none;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<input type="text" />
<button id="btnConv">Convert</button>
<button id="btnDeConv">DeConvert</button>

ALSO SEE:

Base64 - MDN Web Docs
Determine if a string is in Base64 in JavaScript

Elle answered 23/8, 2017 at 16:24 Comment(6)
To confirm, this supports UTF-8 characters?Triplenerved
@Triplenerved I realize this is two years too late, but yes it does. I am also just realizing as I type this that you provided an edit that possibly made UTF8 work.Coeternity
For anyone here looking for a good solution to use with Node.js, I can confirm this works. For decoding in Node, I used: Buffer.from(b64data, 'base64').toString();Coeternity
Can someone explain what unescape and escape are doing in this solution snipped?Touchwood
@Sammi MDN has the explanationthis is absolutely the way to do it right.Sacci
For frontend: I noticed that both btoa & unescape & escape are flagged as depreciated. This has more info: https://mcmap.net/q/42204/-convert-a-string-to-base64-in-javascript-btoa-and-atob-are-deprecated/4096078 & https://mcmap.net/q/42205/-deprecation-of-javascript-function-unescape-closedEady
W
29

If you use Node.js, you can do this:

let a = Buffer.from('JavaScript').toString('base64');
console.log(a);

let b = Buffer.from(a, 'base64').toString();
console.log(b);
Wimsatt answered 7/3, 2021 at 15:26 Comment(2)
This is only for Node.js, not JavaScript in the browserTriplenerved
You are right. Sorry I just test by Node.js.Wimsatt
A
27

There are a couple of bugs in both implementations of _utf8_decode. c1 and c2 are assigned as global variables due to broken use of the var statement, and c3 is not initialized or declared at all.

It works, but these variables will overwrite any existing ones with the same name outside this function.

Here's a version that won't do this:

// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
    var string = "";
    var i = 0;
    var c = 0, c1 = 0, c2 = 0;

    while ( i < utftext.length ) {

        c = utftext.charCodeAt(i);

        if (c < 128) {
            string += String.fromCharCode(c);
            i++;
        }
        else if((c > 191) && (c < 224)) {
            c1 = utftext.charCodeAt(i+1);
            string += String.fromCharCode(((c & 31) << 6) | (c1 & 63));
            i += 2;
        }
        else {
            c1 = utftext.charCodeAt(i+1);
            c2 = utftext.charCodeAt(i+2);
            string += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63));
            i += 3;
        }
    }
    return string;
}
Adamec answered 26/7, 2011 at 21:14 Comment(4)
@Daan I didn't have enough rep to edit answers when I wrote this answer...in 2011.Adamec
IE7 ? i guess we should stop wasting time to write code for that, people won't stop using this old technology unless we developers forced them to!Snake
@RonanDejhero does it not work in IE7? I don't remember if I tested in that particular browser.Adamec
What i meant that if it does not work in IE7, no one should care!. i didn't test and won't test it :)Snake
B
25

For newer browsers you can use the followings.

const base64 = {
    decode: s => Uint8Array.from(atob(s), c => c.charCodeAt(0)),
    encode: b => btoa(String.fromCharCode(...new Uint8Array(b))),
    decodeToString: s => new TextDecoder().decode(base64.decode(s)),
    encodeString: s => base64.encode(new TextEncoder().encode(s)),
};

For Node.js you can use the following to encode string, Buffer, or Uint8Array to string, and decode from string, Buffer, or Uint8Array to Buffer.

const base64 = {
    decode: s => Buffer.from(s, 'base64'),
    encode: b => Buffer.from(b).toString('base64')
};
Bink answered 9/11, 2018 at 7:15 Comment(2)
on Firefox, base64.encode("test").length returns 0 :oSura
@Sura base64.encode only guarantees to work when encoding TypedArray inputs. But for completeness let me add more variants for string input/output.Bink
M
23

This question and its answers pointed me in the right direction. Especially with Unicode, atob and btoa can not be used "vanilla" and these days everything is Unicode...

Directly from Mozilla, two nice functions for this purpose.
Tested with Unicode and HTML tags inside:

function b64EncodeUnicode(str) {
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
        return String.fromCharCode('0x' + p1);
    }));
}

b64EncodeUnicode('✓ à la mode'); // "4pyTIMOgIGxhIG1vZGU="
b64EncodeUnicode('\n'); // "Cg=="


function b64DecodeUnicode(str) {
    return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}

b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"
b64DecodeUnicode('Cg=='); // "\n"

These functions will perform lightning fast in comparison to raw Base64 decoding using a custom JavaScript function as btoa and atob are executed outside the interpreter.

If you can ignore old Internet Explorer and old mobile phones (like iPhone 3?) this should be a good solution.

Manfred answered 3/11, 2016 at 2:24 Comment(4)
Is this from MDN?Oteliaotero
developer.mozilla.org/en-US/docs/Web/API/…Manfred
This answer saved my sanity. For additional info - after using "b64EncodeUnicode()", you can safely use "base64_decode()" in PHP on the resulting string.Baldachin
(From MDN) The simplest solution is to use TextEncoder and TextDecoder to convert between UTF-8 and single-byte representations of the string: developer.mozilla.org/en-US/docs/Glossary/…Mala
D
15

Basically I've just cleaned up the original code a little so JSLint doesn't complain quite as much, and I made the methods marked as private in the comments actually private. I also added two methods I needed in my own project, namely decodeToHex and encodeFromHex.

The code:

var Base64 = (function() {
    "use strict";

    var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

    var _utf8_encode = function (string) {

        var utftext = "", c, n;

        string = string.replace(/\r\n/g,"\n");

        for (n = 0; n < string.length; n++) {

            c = string.charCodeAt(n);

            if (c < 128) {

                utftext += String.fromCharCode(c);

            } else if((c > 127) && (c < 2048)) {

                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            } else {

                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    };

    var _utf8_decode = function (utftext) {
        var string = "", i = 0, c = 0, c1 = 0, c2 = 0;

        while ( i < utftext.length ) {

            c = utftext.charCodeAt(i);

            if (c < 128) {

                string += String.fromCharCode(c);
                i++;

            } else if((c > 191) && (c < 224)) {

                c1 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c1 & 63));
                i += 2;

            } else {

                c1 = utftext.charCodeAt(i+1);
                c2 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63));
                i += 3;
            }
        }
        return string;
    };

    var _hexEncode = function(input) {
        var output = '', i;

        for(i = 0; i < input.length; i++) {
            output += input.charCodeAt(i).toString(16);
        }

        return output;
    };

    var _hexDecode = function(input) {
        var output = '', i;

        if(input.length % 2 > 0) {
            input = '0' + input;
        }

        for(i = 0; i < input.length; i = i + 2) {
            output += String.fromCharCode(parseInt(input.charAt(i) + input.charAt(i + 1), 16));
        }
        return output;
    };

    var encode = function (input) {
        var output = "", chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;

        input = _utf8_encode(input);

        while (i < input.length) {

            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output += _keyStr.charAt(enc1);
            output += _keyStr.charAt(enc2);
            output += _keyStr.charAt(enc3);
            output += _keyStr.charAt(enc4);
        }
        return output;
    };

    var decode = function (input) {
        var output = "", chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {

            enc1 = _keyStr.indexOf(input.charAt(i++));
            enc2 = _keyStr.indexOf(input.charAt(i++));
            enc3 = _keyStr.indexOf(input.charAt(i++));
            enc4 = _keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output += String.fromCharCode(chr1);

            if (enc3 !== 64) {
                output += String.fromCharCode(chr2);
            }
            if (enc4 !== 64) {
                output += String.fromCharCode(chr3);
            }

        }

        return _utf8_decode(output);
    };

    var decodeToHex = function(input) {
        return _hexEncode(decode(input));
    };

    var encodeFromHex = function(input) {
        return encode(_hexDecode(input));
    };

    return {
        'encode': encode,
        'decode': decode,
        'decodeToHex': decodeToHex,
        'encodeFromHex': encodeFromHex
    };
}());
Degreeday answered 20/8, 2012 at 16:7 Comment(3)
I initially thought your unrolling of the output concatenation into separate statements would be more optimal, but after I thought about it for a sec, this should be more inefficient since Javascript strings are immutable and it would cause 4 copies of potentially huge data blobs when working with large binary data files. It is a safer bet to concatenate the 4 chars together first and then building a new string. I wish I knew for certain of a better string building method that would be sure to be efficient on all platforms. (even IE6)Fachanan
I haven't considered performance in my cleanup of the originally posted code. I just made it more readable and made methods marked as private in the comments in the original actually be private by using the revealing module pattern. I'm sure it can be optimized in regards to performance as well. Not quite sure when garbage collection would kick in here, and hashing large files through Javascript isn't very common (or indeed likely not the optimal solution in any case).Degreeday
Funny how this code sort of lives here. There are already 3 different versions of it on this page.Orourke
H
12

To make a Base64 encoded String URL friendly, in JavaScript you could do something like this:

// if this is your Base64 encoded string
var str = 'VGhpcyBpcyBhbiBhd2Vzb21lIHNjcmlwdA=='; 

// make URL friendly:
str = str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');

// reverse to original encoding
str = (str + '===').slice(0, str.length + (str.length % 4));
str = str.replace(/-/g, '+').replace(/_/g, '/');

See also this Fiddle: http://jsfiddle.net/magikMaker/7bjaT/

Hypostatize answered 21/8, 2011 at 15:7 Comment(2)
I would humbly suggest that the use of encodeURIComponent may well result in a superior outcome with less expenditure of effort on the part of the developer.Bunko
encodeURIComponent will change the length of base64 encoded strings, and replacing '-' and '_' with '+' and '/' is standard practice when using base64 in URLs (e.g. docs.python.org/library/base64.html#base64.urlsafe_b64encode). No need to get upset.Gorrono
Y
12

Please note that this is not suitable for raw Unicode strings! See the Unicode section here.

Syntax for encoding

var encodedData = window.btoa(stringToEncode);

Syntax for decoding

var decodedData = window.atob(encodedData);

Younker answered 4/2, 2013 at 12:39 Comment(1)
Direct link to the unicode section: developer.mozilla.org/en-US/docs/Web/API/…Trypsin
A
11

I have rewritten these encoding and decoding methods by hand with the exception of the hexadecimal one into a modular format for cross-platform / browser compatibility and also with real private scoping, and uses btoa and atob if they exist due to speed rather than utilize its own encoding:

https://gist.github.com/Nijikokun/5192472

Usage:

base64.encode(/* String */);
base64.decode(/* String */);

utf8.encode(/* String */);
utf8.decode(/* String */);
Appendix answered 19/3, 2013 at 0:56 Comment(0)
P
10

If you need to encode an HTML image object, you can write a simple function like:

function getBase64Image(img) {
  var canvas = document.createElement("canvas");
  canvas.width = img.width;
  canvas.height = img.height;
  var ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  var dataURL = canvas.toDataURL("image/png");
  // escape data:image prefix
  return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
  // or just return dataURL
  // return dataURL
}

To get the Base64 encoding of the image by id:

function getBase64ImageById(id){
  return getBase64Image(document.getElementById(id));
}

More is here.

Palish answered 12/1, 2013 at 22:28 Comment(1)
Yep, and var img = new Image(); img.src = "../images/myPic.png";Satisfied
T
10

2022 deprecation warning update

I saw deprecation warning on my vscode

This function is only provided for compatibility with legacy web platform APIs and should never be used in new code, 
because they use strings to represent binary data and predate the introduction of typed arrays in JavaScript. 
For code running using Node.js APIs, 
converting between base64-encoded strings and binary data should be performed using Buffer.from(str, 'base64') andbuf.toString('base64').

After searching a bit more, I found this issue that says it isn't deprecated

https://github.com/microsoft/TypeScript/issues/45566

so the solution to the deprecation warning on web JS, use window.btoa and the warning will disappear.

Tectrix answered 8/2, 2022 at 10:53 Comment(0)
I
9

You can use window.btoa and window.atob...

const encoded = window.btoa('Alireza Dezfoolian'); // encode a string
const decoded = window.atob(encoded); // decode the string

Probably using the way which MDN is can do your job the best... Also accepting Unicode... using these two simple functions:

// UCS-2 string to Base64 encoded ASCII
function utoa(str) {
    return window.btoa(unescape(encodeURIComponent(str)));
}
// Base64 encoded ASCII to UCS-2 string
function atou(str) {
    return decodeURIComponent(escape(window.atob(str)));
}
// Usage:
utoa('✓ à la mode'); // 4pyTIMOgIGxhIG1vZGU=
atou('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"

utoa('I \u2661 Unicode!'); // SSDimaEgVW5pY29kZSE=
atou('SSDimaEgVW5pY29kZSE='); // "I ♡ Unicode!"
Impoverished answered 15/10, 2018 at 11:23 Comment(1)
MDN is the way to go. ReferenceSacci
K
8

I needed encoding of an UTF-8 string as Base64 for a project of mine. Most of the answers here don't seem to properly handle UTF-16 surrogate pairs when converting to UTF-8 so, for completion sake, I will post my solution:

function strToUTF8Base64(str) {

    function decodeSurrogatePair(hi, lo) {
        var resultChar = 0x010000;
        resultChar += lo - 0xDC00;
        resultChar += (hi - 0xD800) << 10;
        return resultChar;
    }

    var bytes = [0, 0, 0];
    var byteIndex = 0;
    var result = [];

    function output(s) {
        result.push(s);
    }

    function emitBase64() {

        var digits =
                'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
                'abcdefghijklmnopqrstuvwxyz' +
                '0123456789+/';

        function toDigit(value) {
            return digits[value];
        }

        // --Byte 0--    --Byte 1--    --Byte 2--
        // 1111  1122    2222  3333    3344  4444

        var d1 = toDigit(bytes[0] >> 2);
        var d2 = toDigit(
            ((bytes[0] & 0x03) << 4) |
            (bytes[1] >> 4));
        var d3 = toDigit(
            ((bytes[1] & 0x0F) << 2) |
            (bytes[2] >> 6));
        var d4 = toDigit(
            bytes[2] & 0x3F);

        if (byteIndex === 1) {
            output(d1 + d2 + '==');
        }
        else if (byteIndex === 2) {
            output(d1 + d2 + d3 + '=');
        }
        else {
            output(d1 + d2 + d3 + d4);
        }
    }

    function emit(chr) {
        bytes[byteIndex++] = chr;
        if (byteIndex == 3) {
            emitBase64();
            bytes[0] = 0;
            bytes[1] = 0;
            bytes[2] = 0;
            byteIndex = 0;
        }
    }

    function emitLast() {
        if (byteIndex > 0) {
            emitBase64();
        }
    }

    // Converts the string to UTF8:

    var i, chr;
    var hi, lo;
    for (i = 0; i < str.length; i++) {
        chr = str.charCodeAt(i);

        // Test and decode surrogate pairs in the string
        if (chr >= 0xD800 && chr <= 0xDBFF) {
            hi = chr;
            lo = str.charCodeAt(i + 1);
            if (lo >= 0xDC00 && lo <= 0xDFFF) {
                chr = decodeSurrogatePair(hi, lo);
                i++;
            }
        }

        // Encode the character as UTF-8.
        if (chr < 0x80) {
            emit(chr);
        }
        else if (chr < 0x0800) {
            emit((chr >> 6) | 0xC0);
            emit(((chr >> 0) & 0x3F) | 0x80);
        }
        else if (chr < 0x10000) {
            emit((chr >> 12) | 0xE0);
            emit(((chr >>  6) & 0x3F) | 0x80);
            emit(((chr >>  0) & 0x3F) | 0x80);
        }
        else if (chr < 0x110000) {
            emit((chr >> 18) | 0xF0);
            emit(((chr >> 12) & 0x3F) | 0x80);
            emit(((chr >>  6) & 0x3F) | 0x80);
            emit(((chr >>  0) & 0x3F) | 0x80);
        }
    }

    emitLast();

    return result.join('');
}

Note that the code is not thoroughly tested. I tested some inputs, including things like strToUTF8Base64('衠衢蠩蠨') and compared with the output of an online encoding tool (https://www.base64encode.org/).

Keratoplasty answered 4/10, 2014 at 18:34 Comment(1)
This is a great job! But instead of "return digits[value]" it should be "return digits.substr(value,1)" in function emitBase64().becose error/ Stumbles over emoji and multibyte characters. But it often encodes what online decoders cannot decode! This code brought out the basic Cyrillic alphabet without problems.Icarian
P
6

I'd rather use the Base64 encode/decode methods from CryptoJS, the most popular library for standard and secure cryptographic algorithms implemented in JavaScript using best practices and patterns.

Paedogenesis answered 17/11, 2013 at 7:5 Comment(0)
H
6

Here is a minified polyfill for window.atob + window.btoa:

(function(){function t(t){this.message=t}var e="undefined"!=typeof exports?exports:this,r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";t.prototype=Error(),t.prototype.name="InvalidCharacterError",e.btoa||(e.btoa=function(e){for(var o,n,a=0,i=r,c="";e.charAt(0|a)||(i="=",a%1);c+=i.charAt(63&o>>8-8*(a%1))){if(n=e.charCodeAt(a+=.75),n>255)throw new t("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");o=o<<8|n}return c}),e.atob||(e.atob=function(e){if(e=e.replace(/=+$/,""),1==e.length%4)throw new t("'atob' failed: The string to be decoded is not correctly encoded.");for(var o,n,a=0,i=0,c="";n=e.charAt(i++);~n&&(o=a%4?64*o+n:n,a++%4)?c+=String.fromCharCode(255&o>>(6&-2*a)):0)n=r.indexOf(n);return c})})();
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], function() {factory(root);});
    } else factory(root);
// node.js has always supported base64 conversions, while browsers that support
// web workers support base64 too, but you may never know.
})(typeof exports !== "undefined" ? exports : this, function(root) {
    if (root.atob) {
        // Some browsers' implementation of atob doesn't support whitespaces
        // in the encoded string (notably, IE). This wraps the native atob
        // in a function that strips the whitespaces.
        // The original function can be retrieved in atob.original
        try {
            root.atob(" ");
        } catch(e) {
            root.atob = (function(atob) {
                var func = function(string) {
                    return atob(String(string).replace(/[\t\n\f\r ]+/g, ""));
                };
                func.original = atob;
                return func;
            })(root.atob);
        }
        return;
    }

        // base64 character set, plus padding character (=)
    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
        // Regular expression to check formal correctness of base64 encoded strings
        b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;

    root.btoa = function(string) {
        string = String(string);
        var bitmap, a, b, c,
            result = "", i = 0,
            rest = string.length % 3; // To determine the final padding

        for (; i < string.length;) {
            if ((a = string.charCodeAt(i++)) > 255
                    || (b = string.charCodeAt(i++)) > 255
                    || (c = string.charCodeAt(i++)) > 255)
                throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.");

            bitmap = (a << 16) | (b << 8) | c;
            result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63)
                    + b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63);
        }

        // If there's need of padding, replace the last 'A's with equal signs
        return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result;
    };

    root.atob = function(string) {
        // atob can work with strings with whitespaces, even inside the encoded part,
        // but only \t, \n, \f, \r and ' ', which can be stripped.
        string = String(string).replace(/[\t\n\f\r ]+/g, "");
        if (!b64re.test(string))
            throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");

        // Adding the padding if missing, for semplicity
        string += "==".slice(2 - (string.length & 3));
        var bitmap, result = "", r1, r2, i = 0;
        for (; i < string.length;) {
            bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12
                    | (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++)));

            result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255)
                    : r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255)
                    : String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255);
        }
        return result;
    };
});

Full version from https://github.com/MaxArt2501/base64-js/blob/master/base64.js

Hawken answered 20/11, 2013 at 8:24 Comment(1)
Perhaps provide a normally formatted version as well?Debor
I
6

Use the js-base64 library as

btoa() doesn't work with emojis

var str = "I was funny 😂";
console.log("Original string:", str);

var encodedStr = Base64.encode(str)
console.log("Encoded string:", encodedStr);

var decodedStr = Base64.decode(encodedStr)
console.log("Decoded string:", decodedStr);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/base64.min.js"></script>
Intercession answered 18/3, 2020 at 5:30 Comment(0)
F
5

Well, if you are using Dojo. It gives us direct way to encode or decode into Base64.

Try this:

To encode an array of bytes using dojox.encoding.base64:

var str = dojox.encoding.base64.encode(myByteArray);

To decode a Base64-encoded string:

var bytes = dojox.encoding.base64.decode(str);
Fortitude answered 29/8, 2013 at 14:10 Comment(0)
C
5

Here is an AngularJS Factory version of @user850789's one:

'use strict';

var ProjectNameBase64Factory = angular.module('project_name.factories.base64', []);

ProjectNameBase64Factory.factory('Base64', function () {
    var Base64 = {
        // private property
        _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

        // public method for encoding
        encode: function (input) {
            var output = "";
            var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            var i = 0;

            input = Base64._utf8_encode(input);

            while (i < input.length) {

                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }

                output = output +
                         Base64._keyStr.charAt(enc1) + Base64._keyStr.charAt(enc2) +
                         Base64._keyStr.charAt(enc3) + Base64._keyStr.charAt(enc4);

            }

            return output;
        },

        // public method for decoding
        decode: function (input) {
            var output = "";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;

            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

            while (i < input.length) {

                enc1 = Base64._keyStr.indexOf(input.charAt(i++));
                enc2 = Base64._keyStr.indexOf(input.charAt(i++));
                enc3 = Base64._keyStr.indexOf(input.charAt(i++));
                enc4 = Base64._keyStr.indexOf(input.charAt(i++));

                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;

                output = output + String.fromCharCode(chr1);

                if (enc3 != 64) {
                    output = output + String.fromCharCode(chr2);
                }
                if (enc4 != 64) {
                    output = output + String.fromCharCode(chr3);
                }

            }

            output = Base64._utf8_decode(output);

            return output;

        },

        // private method for UTF-8 encoding
        _utf8_encode: function (string) {
            string = string.replace(/\r\n/g, "\n");
            var utftext = "";

            for (var n = 0; n < string.length; n++) {

                var c = string.charCodeAt(n);

                if (c < 128) {
                    utftext += String.fromCharCode(c);
                }
                else if ((c > 127) && (c < 2048)) {
                    utftext += String.fromCharCode((c >> 6) | 192);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
                else {
                    utftext += String.fromCharCode((c >> 12) | 224);
                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                    utftext += String.fromCharCode((c & 63) | 128);
                }

            }

            return utftext;
        },

        // private method for UTF-8 decoding
        _utf8_decode: function (utftext) {
            var string = "";
            var i = 0;
            var c = 0, c2 = 0, c3 = 0;

            while (i < utftext.length) {

                c = utftext.charCodeAt(i);

                if (c < 128) {
                    string += String.fromCharCode(c);
                    i++;
                }
                else if ((c > 191) && (c < 224)) {
                    c2 = utftext.charCodeAt(i + 1);
                    string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                    i += 2;
                }
                else {
                    c2 = utftext.charCodeAt(i + 1);
                    c3 = utftext.charCodeAt(i + 2);
                    string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                    i += 3;
                }

            }
            return string;
        }
    };
    return Base64;
});
Coleencolella answered 25/5, 2014 at 4:27 Comment(2)
What is "AngularJS Factory"? E.g., is it actually a proper noun?Debor
en.wikipedia.org/wiki/Factory_method_patternColeencolella
T
5

While a bit more work, if you want a high performance native solution there are some HTML5 functions you can use.

If you can get your data into a Blob, then you can use the FileReader.readAsDataURL() function to get a data:// URL and chop off the front of it to get at the Base64 data.

You may have to do further processing however to urldecode the data, as I'm not sure whether + characters are escaped or not for the data:// URL, but this should be pretty trivial.

Testaceous answered 21/7, 2016 at 1:11 Comment(0)
E
5

Here is a LIVE DEMO of atob() and btoa() JavaScript built-in functions:

<!DOCTYPE html>
<html>
  <head>
    <style>
      textarea{
        width:30%;
        height:100px;
      }
    </style>
    <script>
      // encode string to base64
      function encode()
      {
        var txt = document.getElementById("txt1").value;
        var result = btoa(txt);
        document.getElementById("txt2").value = result;
      }
      // decode base64 back to original string
      function decode()
      {
        var txt = document.getElementById("txt3").value;
        var result = atob(txt);
        document.getElementById("txt4").value = result;
      }
    </script>
  </head>
  <body>
    <div>
      <textarea id="txt1">Some text to decode
      </textarea>
    </div>
    <div>
      <input type="button" id="btnencode" value="Encode" onClick="encode()"/>
    </div>
    <div>
      <textarea id="txt2">
      </textarea>
    </div>
    <br/>
    <div>
      <textarea id="txt3">U29tZSB0ZXh0IHRvIGRlY29kZQ==
      </textarea>
    </div>
    <div>
      <input type="button" id="btndecode" value="Decode" onClick="decode()"/>
    </div>
    <div>
      <textarea id="txt4">
      </textarea>
    </div>
  </body>
</html>
Eastman answered 9/2, 2019 at 10:20 Comment(0)
F
5

When I use

btoa("☸☹☺☻☼☾☿"))

I get:

Error InvalidCharacterError: The string to be encoded contains characters outside of the Latin1 range.

I found documentation, Unicode strings, was providing a solution as below.

function toBinary(string) {
  const codeUnits = new Uint16Array(string.length);
  for (let i = 0; i < codeUnits.length; i++) {
    codeUnits[i] = string.charCodeAt(i);
  }
  return String.fromCharCode(...new Uint8Array(codeUnits.buffer));
}

function fromBinary(binary) {
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < bytes.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return String.fromCharCode(...new Uint16Array(bytes.buffer));
}

const myString = "☸☹☺☻☼☾☿"
// console.log(btoa(myString)) // Error InvalidCharacterError: The string to be encoded contains characters outside of the Latin1 range.
const converted = toBinary(myString)
const encoded = btoa(converted)
console.log(encoded)

const decoded = atob(encoded)
const original = fromBinary(decoded)
console.log(original);
Flighty answered 6/5, 2021 at 9:47 Comment(1)
I'm just copying and pasting to help people who don't want to click on the link...Flighty
B
4

For my project I still need to support IE7 and work with large input to encode.

Based on the code proposed by Joe Dyndale and as suggested in comment by Marius, it is possible to improve the performance with IE7 by constructing the result with an array instead of a string.

Here is the example for encode:

var encode = function (input) {
    var output = [], chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;

    input = _utf8_encode(input);

    while (i < input.length) {

        chr1 = input.charCodeAt(i++);
        chr2 = input.charCodeAt(i++);
        chr3 = input.charCodeAt(i++);

        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
        enc4 = chr3 & 63;

        if (isNaN(chr2)) {
            enc3 = enc4 = 64;
        } else if (isNaN(chr3)) {
            enc4 = 64;
        }

        output.push(_keyStr.charAt(enc1));
        output.push(_keyStr.charAt(enc2));
        output.push(_keyStr.charAt(enc3));
        output.push(_keyStr.charAt(enc4));

    }

    return output.join("");
};
Benedetta answered 24/7, 2013 at 6:52 Comment(0)
F
2

JavaScript without the btoa middlestep (no library)

In the question title you write about string conversion, but in the question you talk about binary data (picture) so here is a function which makes a proper conversion starting from PNG picture binary data (details and reversal conversion are here).

Enter image description here

function bytesArrToBase64(arr) {
  const abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // base64 alphabet
  const bin = n => n.toString(2).padStart(8,0); // convert num to 8-bit binary string
  const l = arr.length
  let result = '';

  for(let i=0; i<=(l-1)/3; i++) {
    let c1 = i*3+1>=l; // case when "=" is on end
    let c2 = i*3+2>=l; // case when "=" is on end
    let chunk = bin(arr[3*i]) + bin(c1? 0:arr[3*i+1]) + bin(c2? 0:arr[3*i+2]);
    let r = chunk.match(/.{1,6}/g).map((x,j)=> j==3&&c2 ? '=' :(j==2&&c1 ? '=':abc[+('0b'+x)]));
    result += r.join('');
  }

  return result;
}



// TEST

const pic = [ // PNG binary data
    0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
    0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
    0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
    0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
    0x01, 0x59, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f,
    0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65,
    0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22,
    0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74,
    0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d,
    0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e,
    0x34, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64,
    0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a,
    0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
    0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31,
    0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64,
    0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23,
    0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64,
    0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
    0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d,
    0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66,
    0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73,
    0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74,
    0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66,
    0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f,
    0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72,
    0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44,
    0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a,
    0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46,
    0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74,
    0x61, 0x3e, 0x0a, 0x4c, 0xc2, 0x27, 0x59, 0x00, 0x00, 0x00, 0xf9, 0x49,
    0x44, 0x41, 0x54, 0x38, 0x11, 0x95, 0x93, 0x3d, 0x0a, 0x02, 0x41, 0x0c,
    0x85, 0xb3, 0xb2, 0x85, 0xb7, 0x10, 0x6c, 0x04, 0x1b, 0x0b, 0x4b, 0x6f,
    0xe2, 0x76, 0x1e, 0xc1, 0xc2, 0x56, 0x6c, 0x2d, 0xbc, 0x85, 0xde, 0xc4,
    0xd2, 0x56, 0xb0, 0x11, 0xbc, 0x85, 0x85, 0xa0, 0xfb, 0x46, 0xbf, 0xd9,
    0x30, 0x33, 0x88, 0x06, 0x76, 0x93, 0x79, 0x93, 0xf7, 0x92, 0xf9, 0xab,
    0xcc, 0xec, 0xd9, 0x7e, 0x7f, 0xd9, 0x63, 0x33, 0x8e, 0xf9, 0x75, 0x8c,
    0x92, 0xe0, 0x34, 0xe8, 0x27, 0x88, 0xd9, 0xf4, 0x76, 0xcf, 0xb0, 0xaa,
    0x45, 0xb2, 0x0e, 0x4a, 0xe4, 0x94, 0x39, 0x59, 0x0c, 0x03, 0x54, 0x14,
    0x58, 0xce, 0xbb, 0xea, 0xdb, 0xd1, 0x3b, 0x71, 0x75, 0xb9, 0x9a, 0xe2,
    0x7a, 0x7d, 0x36, 0x3f, 0xdf, 0x4b, 0x95, 0x35, 0x09, 0x09, 0xef, 0x73,
    0xfc, 0xfa, 0x85, 0x67, 0x02, 0x3e, 0x59, 0x55, 0x31, 0x89, 0x31, 0x56,
    0x8c, 0x78, 0xb6, 0x04, 0xda, 0x23, 0x01, 0x01, 0xc8, 0x8c, 0xe5, 0x77,
    0x87, 0xbb, 0x65, 0x02, 0x24, 0xa4, 0xad, 0x82, 0xcb, 0x4b, 0x4c, 0x64,
    0x59, 0x14, 0xa0, 0x72, 0x40, 0x3f, 0xbf, 0xe6, 0x68, 0xb6, 0x9f, 0x75,
    0x08, 0x63, 0xc8, 0x9a, 0x09, 0x02, 0x25, 0x32, 0x34, 0x48, 0x7e, 0xcc,
    0x7d, 0x10, 0xaf, 0xa6, 0xd5, 0xd2, 0x1a, 0x3d, 0x89, 0x38, 0xf5, 0xf1,
    0x14, 0xb4, 0x69, 0x6a, 0x4d, 0x15, 0xf5, 0xc9, 0xf0, 0x5c, 0x1a, 0x61,
    0x8a, 0x75, 0xd1, 0xe8, 0x3a, 0x2c, 0x41, 0x5d, 0x70, 0x41, 0x20, 0x29,
    0xf9, 0x9b, 0xb1, 0x37, 0xc5, 0x4d, 0xfc, 0x45, 0x84, 0x7d, 0x08, 0x8f,
    0x89, 0x76, 0x54, 0xf1, 0x1b, 0x19, 0x92, 0xef, 0x2c, 0xbe, 0x46, 0x8e,
    0xa6, 0x49, 0x5e, 0x61, 0x89, 0xe4, 0x05, 0x5e, 0x4e, 0xa4, 0x5c, 0x10,
    0x6e, 0x9f, 0xfc, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
    0xae, 0x42, 0x60, 0x82
];

let b64pic = bytesArrToBase64(pic);
myPic.src = "data:image/png;base64,"+b64pic;
msg.innerHTML = "Base64 encoded pic data:<br>" + b64pic;
img { zoom: 10; image-rendering: pixelated; }
#msg { word-break: break-all; }
<img id="myPic">
<code id="msg"></code>
Faustena answered 27/7, 2020 at 11:43 Comment(0)
H
1

Here is helper funktion to encode to base64url:

base64url (s) {
        var to64url = btao(s);
         // Replace non-url compatible chars with base64url standard chars and remove leading =
        return to64url.replace(/\+/g, '_').replace(/\//g, '-').replace(/=+$/g, '');
    }
Hawkins answered 19/5, 2022 at 5:39 Comment(1)
btoa not btaoVanthe
L
0

You can use btoa()/atob() in browser, but some improvements required, as described here https://base64tool.com/uncaught-domexception-btoa-on-window/ and there https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa for UTF strings support!

Loanloanda answered 21/6, 2020 at 18:34 Comment(0)
H
0

all the solutions here seems to fail in some cases and are pretty complex to understand. Especially for non-latin languages such as ARABIC

here's a short solution that decodes UTF-16

//decodes utf-16 characters such as ARABIC, URDU,PASHTO text
function decodeBase64(s) {
    var percentEncodedStr = atob(s).split('').map(function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join('');
    return decodeURIComponent(percentEncodedStr);
}
Houstonhoustonia answered 28/1, 2022 at 12:16 Comment(0)
H
-1

Incase anyone wants to encode a string to base64 in Typescript

I tweaked Peter Mortensen's answer and added types ( where needed )

const Base64 = {


_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

// added type string to input
encode: function(input: string) {
    let output = "";
    let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    let i = 0;

    input = Base64._utf8_encode(input);

    while (i < input.length) {

        chr1 = input.charCodeAt(i++);
        chr2 = input.charCodeAt(i++);
        chr3 = input.charCodeAt(i++);

        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
        enc4 = chr3 & 63;

        if (isNaN(chr2)) {
            enc3 = enc4 = 64;
        } else if (isNaN(chr3)) {
            enc4 = 64;
        }

        output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

    }

    return output;
},
// added type string to input
_utf8_encode: function(input: string) {
    input= input.replace(/\r\n/g, "\n");
    let utftext = "";

    for (let n = 0; n < input.length; n++) {

        let c = input.charCodeAt(n);

        if (c < 128) {
            utftext += String.fromCharCode(c);
        }
        else if ((c > 127) && (c < 2048)) {
            utftext += String.fromCharCode((c >> 6) | 192);
            utftext += String.fromCharCode((c & 63) | 128);
        }
        else {
            utftext += String.fromCharCode((c >> 12) | 224);
            utftext += String.fromCharCode(((c >> 6) & 63) | 128);
            utftext += String.fromCharCode((c & 63) | 128);
        }

    }

    return utftext;
  },
}
 
// function that consumes that Base64.encode function
const encode = ( cipher : string) => {

  const encodedString: string = Base64.encode(cipher)

  return encodedString;
}

console.log(encode('Hello World!'))

jsfiddle link

Henrion answered 30/3, 2022 at 7:10 Comment(0)
W
-3

If you want to encode base 64 easy and fast without caring about possible compatibility issues, you can use the turbocommons library. Just download the minified js file and write the following code:

<script src="../yourpathtothelibrary/turbocommons-es5.js"></script>

<script>
    var ConversionUtils = org_turbocommons.ConversionUtils;

    ConversionUtils.stringToBase64('hello');
</script>

You can look at the code on how it is done by the library here:

https://github.com/edertone/TurboCommons/blob/master/TurboCommons-TS/src/main/ts/utils/ConversionUtils.ts

More info here:

https://turboframework.org/en/blog/2022-10-26/encode-decode-base64-strings-javascript-typescript-php

And you can test it online here:

https://turboframework.org/en/app/stringutils/base64-encode

Wolfram answered 25/10, 2022 at 20:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.