1276

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.

Sandra Rossi
13.9k6 gold badges27 silver badges58 bronze badges
asked Oct 29, 2008 at 13:34
4

32 Answers 32

1
2
1410
+250

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:

Lukas Liesis
26.6k12 gold badges122 silver badges123 bronze badges
answered Oct 29, 2008 at 15:31

24 Comments

Note that this also works for webkit browsers, such as Safari.
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.
See my edit, @Triynko. These are not intended to be used to process text, period.
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.
It's pronounced b to a and a to b, b standing for binary and a standing for ASCII
|
329

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.

Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Oct 29, 2008 at 13:39

15 Comments

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.
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.
If you use code from webtoolkito info do not forget about copyright: /** * * Base64 encode / decode * webtoolkit.info * **/
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.
@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.
|
291

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=');
}]);
surfmuggle
6,0388 gold badges58 silver badges96 bronze badges
answered Oct 22, 2014 at 18:06

4 Comments

This answer is based on the original code and DOES NOT include updates to that code posted in other answers here.
Proposed NodeJS solution is deprecated.
I converted @davidcondrey's cross-browser Base64 object to be compatible with TypeScript. Available here: Github Gist
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).
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;
 }
}
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Jul 18, 2011 at 22:08

5 Comments

ooh my bad, I was taking the input from browser URL; where | is converted to %7C; hence the encoding also wrong.
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?
"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.
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?
@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.
100

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

answered Aug 23, 2017 at 16:24

6 Comments

To confirm, this supports UTF-8 characters?
@Crashalot 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.
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();
Can someone explain what unescape and escape are doing in this solution snipped?
For frontend: I noticed that both btoa & unescape & escape are flagged as depreciated. This has more info: stackoverflow.com/q/68849233/4096078 & stackoverflow.com/a/27926636/4096078
|
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=");
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Dec 27, 2011 at 3:27

8 Comments

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 jQuery
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.
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.
@Risadinha - 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...
jQuery was not requested. Not a valid answer to a plain old JS question.
|
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);
answered Mar 7, 2021 at 15:26

2 Comments

This is only for Node.js, not JavaScript in the browser
You are right. Sorry I just test by Node.js.
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;
}
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Jul 26, 2011 at 21:14

4 Comments

@Daan I didn't have enough rep to edit answers when I wrote this answer...in 2011.
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!
@RonanDejhero does it not work in IE7? I don't remember if I tested in that particular browser.
What i meant that if it does not work in IE7, no one should care!. i didn't test and won't test it :)
27

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')
};
answered Nov 9, 2018 at 7:15

2 Comments

on Firefox, base64.encode("test").length returns 0 :o
@hanshenrik base64.encode only guarantees to work when encoding TypedArray inputs. But for completeness let me add more variants for string input/output.
24

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.

answered Nov 3, 2016 at 2:24

4 Comments

Is this from MDN?
This answer saved my sanity. For additional info - after using "b64EncodeUnicode()", you can safely use "base64_decode()" in PHP on the resulting string.
(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/…
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
 };
}());
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Aug 20, 2012 at 16:07

3 Comments

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)
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).
Funny how this code sort of lives here. There are already 3 different versions of it on this page.
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);

Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Feb 4, 2013 at 12:39

1 Comment

Direct link to the unicode section: developer.mozilla.org/en-US/docs/Web/API/…
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 */);
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Mar 19, 2013 at 0:56

Comments

11

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/

answered Aug 21, 2011 at 15:07

2 Comments

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

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.

answered Feb 8, 2022 at 10:53

Comments

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.

Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Jan 12, 2013 at 22:28

1 Comment

Yep, and var img = new Image(); img.src = "../images/myPic.png";
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!"
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Oct 15, 2018 at 11:23

2 Comments

MDN is the way to go. Reference
unescape has been deprecated.
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/).

Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Oct 4, 2014 at 18:34

1 Comment

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

Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Nov 17, 2013 at 7:05

Comments

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

answered Nov 20, 2013 at 8:24

1 Comment

Perhaps provide a normally formatted version as well?
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>

Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Mar 18, 2020 at 5:30

Comments

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;
});
answered May 25, 2014 at 4:27

2 Comments

What is "AngularJS Factory"? E.g., is it actually a proper noun?
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);
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Aug 29, 2013 at 14:10

Comments

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.

Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Jul 21, 2016 at 1:11

1 Comment

I believe this would require writing an async function which is rather unfortunate.
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>
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered Feb 9, 2019 at 10:20

Comments

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);

Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answered May 6, 2021 at 9:47

1 Comment

I'm just copying and pasting to help people who don't want to click on the link...
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("");
};
answered Jul 24, 2013 at 6:52

Comments

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>

luiscla27
6,5452 gold badges47 silver badges56 bronze badges
answered Jul 27, 2020 at 11:43

Comments

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, '');
 }
answered May 19, 2022 at 5:39

1 Comment

btoa not btao
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!

answered Jun 21, 2020 at 18:34

Comments

1
2

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.