Encoding and Decoding Base64 Strings in Node.js

What is Base64 Encoding?

Base64 encoding is a way to convert data (typically binary) into the ASCII character set. It is important to mention here that Base64 is not an encryption or compression technique, although it can sometimes be confused as encryption due to the way it seems to obscure data. In fact, size of a Base64 encoded piece of information is 1.3333 times the actual size of your original data.

Base64 is the most widely used base encoding technique with Base16 and Base32 being the other two commonly used encoding schemes.

How Does Base64 Work?

Converting data to base64 is a multistep process. Here is how it works for strings of text:

  1. Calculate the 8 bit binary version of the input text
  2. Re-group the 8 bit version of the data into multiple chunks of 6 bits
  3. Find the decimal version of each of the 6 bit binary chunk
  4. Find the Base64 symbol for each of the decimal values via a Base64 lookup table

For a better understanding of this concept, let's take a look at an example.

Suppose we have string "Go win" and we want to convert it into Base64 string. The first step is to convert this string into binary. The binary version of "Go win" is:

01000111 01101111 00100000 01110111 01101001 01101110

You can see here that each character is represented by 8 bits. However as we said earlier, Base64 converts the data in 8 bit binary form to chunks of 6 bits. This is because Base64 format only has 64 characters: 26 uppercase alphabet letters, 26 lowercase alphabet letters, 10 numeric characters, and the "+" and "/" symbols for new line.

Base64 doesn't use all the ASCII special characters, but only these few. Note that some implementations of Base64 uses different special characters than "+" and "/".

Coming back to the example, let us break our 8 bit data into chunks of 6 bits.

010001 110110 111100 100000 011101 110110 100101 101110

You won't always be able to divide up the data in to full sets of 6 bits, in which case you'll have to deal with padding.

Now for each chunk above, we have to find its decimal value. These decimal values have been given below:

Binary  Decimal  
010001  17  
110110  54  
111100  60  
100000  32  
011101  29  
110110  54  
100101  37  
101110  46  

Finally we have to look the Base64 value for each of the decimal that we just calculated from binary data. Base64 encoding table looks like this:

Decimal to Base64 Lookup Table

Here you can see that decimal 17 corresponds to "R", and decimal 54 corresponds to "2", and so on. Using this encoding table we can see that the string "Go win" is encoded as "R28gd2lu" using Base64. You can use any online text to Base64 converter to verify this result.

Why use Base64 Encoding?

Sending information in binary format can sometimes be risky since not all applications or network systems can handle raw binary. On the other hand, the ASCII character set is widely known and very simple to handle for most systems.

For instance email servers expect textual data, so ASCII is typically used. Therefore, if you want to send images or any other binary file to an email server you first need to encode it in text-based format, preferably ASCII. This is where Base64 encoding comes extremely handy in converting binary data to the correct formats.

Encoding Base64 Strings with Node.js

The easiest way to encode Base64 strings in Node.js is via the Buffer object. In Node.js, Buffer is a global object which means that you do not need to use require statement in order to use Buffer object in your applications.

Internally Buffer is an immutable array of integers that is also capable of performing many different encodings/decodings. These include to/from UTF-8, UCS2, Base64 or even Hex encodings. As you write code that deals with and manipulates data, you'll likely be using the Buffer object at some point.

Take a look at the following example. Here we will encode a text string to Base64 using Buffer object. Save the following code in a file "encode-text.js"

'use strict';

let data = 'stackabuse.com';  
let buff = new Buffer(data);  
let base64data = buff.toString('base64');

console.log('"' + data + '" converted to Base64 is "' + base64data + '"');  

In the above script we create a new buffer object and pass it our string that we want to convert to Base64. We then call "toString" method on the buffer object that we just created and passed it "base64" as a parameter. The "toString" method with "base64" as parameter will return data in the form of Base64 string. Run the above code, you shall see the following output.

$ node encode-text.js
"stackabuse.com" converted to Base64 is "c3RhY2thYnVzZS5jb20="

In output we can see Base64 counterpart for the string that we converted to Base64.

Decoding Base64 Strings with Node.js

Decoding Base64 string is quite similar to encoding it. You have to create a new buffer object and pass two parameters to its constructor. The first parameter is the data in Base64 and second parameter is "base64". Then you simply have to call "toString" on the buffer object but this time the parameter passed to the method will be "ascii" because this is the data type that you want your Base64 data to convert to. Take a look at the following code snippet for reference.

'use strict';

let data = 'c3RhY2thYnVzZS5jb20=';  
let buff = new Buffer(data, 'base64');  
let text = buff.toString('ascii');

console.log('"' + data + '" converted from Base64 to ASCII is "' + text + '"');  

Add the data to "ascii.js" file and save it. Here we have used "Tm8gdG8gUmFjaXNt" as the Base64 input data. When this data is decoded it should display "No to Racism". This is because from the last example we know that "No to Racism" is equal to "Tm8gdG8gUmFjaXNt". Run the above code with Node.js. It will display following output.

Encoding Binary Data to Base64 Strings

As mentioned in the beginning of the article, the primary purpose of Base64 encoding is to convert binary data into textual format. Let us see an example where we will convert an image (binary data) into a Base64 string. Take a look at the following example.

'use strict';

const fs = require('fs');

let buff = fs.readFileSync('stack-abuse-logo.png');  
let base64data = buff.toString('base64');

console.log('Image converted to base 64 is:\n\n' + base64data);  

In the above code we load an image into buffer via the readFileSync() method of the fs module. The rest of the process is similar to creating Base64 string from a normal ASCII string.

When you run the above code you will see following output.

$ node encode-image.js
Image converted to Base64 is:

iVBORw0KGgoAAAANSUhEUgAAABkAAAATCAYAAABlcqYFAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAADuUlEQVQ4EbVUTUtcZxR+7ufkXp1SZ4iZRE1EDVQRnTAhowsZMFm40I2rNqUIIev8hvoPQroQXBTqwiAWcd0EglEhiZNajVZrQGXAWAzaZpzMnZn7lXPeeIe5DaWb9Ax33vOec8/znI/3vVI6nfbxP4v8b/iSJIGfzyGfkPi+D13XUalUBL6qqmIvy5+8WuX/r2RCkUzAoIuLi2hqaoLrutjb28P6+josyxJkiqJA07SQXiqVwHaOZYx/itLc3Px9YIxEIlheXsbExATGxsYwMjIiwEdHRwXA/Pw8EokEcrkcDg4OYJomVlZWMDU1JSqfmZlBR0cHbNsOtVoNCHjlTFiSySQMwxAVxONxQbi0tIRMJoPe3l5MT0+jtbUVg4ODYGImY18qlcL4+DhisZjoggCjv1C7uOyenh7Mzs5iY2ND6FQpdnd3sba2JloSjUYxPDyM/v5+TE5OYn9/X9jZtrOzg+3t7WqyAUmoEu419/+HBw9E+eVymbJqAJP39fWBCR3HEU+hUMDQ0JCYGc8um81iYGAAjY2N8DwvwBdraCY8tHhDA1Y3N9Hd3S2yvH37O7RcbsF7AuUsD9+8wdOFBTx/8QJtbW1C5/nMzc3R0D2UyxXk83lRXcAk1V5GCT5sSUGDbeHxy9/EO98M9OOXzT9wfHISxKC1vR0GHfOtrS2g/SouWwU0Xkggu7qO9PUkJFULnbIQyTm6ewu2hF+vnOIIUQwdGlg8f4QF6wvMWBq+pAkaskSnx4FFVUf0CNpcC797KizXQ4oAHhVdXJJ81F7j6kwUynPHlXDPdFB2fRj+KVK0KvT2rbp3uKYryJU11Cke8qqMuOoioeeJ1MPDYxM36m1cNSq4GdFx58RAWvbx8TrXnK4IgR16Em5GK4iqHi5GHHxLgcSDn97WgZPoND+GGZRpPYH85cgiiRQl1ltXxmFFQ5PuopP8TrW5ZyRcWp7AbmkeZefg5+N6PPnbRJdpw/YlfB0vQiPQZwVdZNtFZEVK6D1VTnccJlXzuqTjvOZiq6Rhj2KqLSJsofOHgIl8+t0/qsfDioxmSUWGjrRFzhYi/5Oynrdl3KXHIZDXtF6hil8R6I9FBV/RvDLnXKxSbAdVYhNeINXBMwmXWCTQGG2Y+Jj+dFrfEmiMAtmeowpo9ojTvkD+A/L1UJUMmiVfkuz6WTyZhFRJAgP33j3bsM5k/Fng68UP21hYJyyxZwLWuS2cKMfUSm3rhD0g4E2g197fwMZ+Bgt8rNe2iP2BhL5dgfFzrx8AfECEDdx45a0AAAAASUVORK5CYII=  

Although the actual image is very small (25x19), the output is still fairly large, partially because Base64 increases the size of the data, as we mentioned earlier.

Decoding Base64 Strings to Binary Data

The reverse process here is very similar to how we decode Base64 strings, as we saw in an earlier section. The biggest difference is the output destination and how data is written there. Let's see the example:

'use strict';

const fs = require('fs');

let data = 'iVBORw0KGgoAAAANSUhEUgAAABkAAAATCAYAAABlcqYFAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAA' +  
'CA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0' +  
'YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly' +  
'93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAg' +  
'ICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZm' +  
'Y6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAADuUlEQVQ4EbVU' +  
'TUtcZxR+7ufkXp1SZ4iZRE1EDVQRnTAhowsZMFm40I2rNqUIIev8hvoPQroQXBTqwiAWcd0EglEhiZNajVZrQGXAWAzaZpzMnZn7lXPeeIe5Da' +  
'Wb9Ax33vOec8/znI/3vVI6nfbxP4v8b/iSJIGfzyGfkPi+D13XUalUBL6qqmIvy5+8WuX/r2RCkUzAoIuLi2hqaoLrutjb28P6+josyxJkiqJA' +  
'07SQXiqVwHaOZYx/itLc3Px9YIxEIlheXsbExATGxsYwMjIiwEdHRwXA/Pw8EokEcrkcDg4OYJomVlZWMDU1JSqfmZlBR0cHbNsOtVoNCHjlTF' +  
'iSySQMwxAVxONxQbi0tIRMJoPe3l5MT0+jtbUVg4ODYGImY18qlcL4+DhisZjoggCjv1C7uOyenh7Mzs5iY2ND6FQpdnd3sba2JloSjUYxPDyM' +  
'/v5+TE5OYn9/X9jZtrOzg+3t7WqyAUmoEu419/+HBw9E+eVymbJqAJP39fWBCR3HEU+hUMDQ0JCYGc8um81iYGAAjY2N8DwvwBdraCY8tHhDA1' +  
'Y3N9Hd3S2yvH37O7RcbsF7AuUsD9+8wdOFBTx/8QJtbW1C5/nMzc3R0D2UyxXk83lRXcAk1V5GCT5sSUGDbeHxy9/EO98M9OOXzT9wfHISxKC1' +  
'vR0GHfOtrS2g/SouWwU0Xkggu7qO9PUkJFULnbIQyTm6ewu2hF+vnOIIUQwdGlg8f4QF6wvMWBq+pAkaskSnx4FFVUf0CNpcC797KizXQ4oAHh' +  
'VdXJJ81F7j6kwUynPHlXDPdFB2fRj+KVK0KvT2rbp3uKYryJU11Cke8qqMuOoioeeJ1MPDYxM36m1cNSq4GdFx58RAWvbx8TrXnK4IgR16Em5G' +  
'K4iqHi5GHHxLgcSDn97WgZPoND+GGZRpPYH85cgiiRQl1ltXxmFFQ5PuopP8TrW5ZyRcWp7AbmkeZefg5+N6PPnbRJdpw/YlfB0vQiPQZwVdZN' +  
'tFZEVK6D1VTnccJlXzuqTjvOZiq6Rhj2KqLSJsofOHgIl8+t0/qsfDioxmSUWGjrRFzhYi/5Oynrdl3KXHIZDXtF6hil8R6I9FBV/RvDLnXKxS' +  
'bAdVYhNeINXBMwmXWCTQGG2Y+Jj+dFrfEmiMAtmeowpo9ojTvkD+A/L1UJUMmiVfkuz6WTyZhFRJAgP33j3bsM5k/Fng68UP21hYJyyxZwLWuS' +  
'2cKMfUSm3rhD0g4E2g197fwMZ+Bgt8rNe2iP2BhL5dgfFzrx8AfECEDdx45a0AAAAASUVORK5CYII=';

let buff = new Buffer(data, 'base64');  
fs.writeFileSync('stack-abuse-logo-out.png', buff);

console.log('Base64 image data converted to file: stack-abuse-logo-out.png');  

Here you can see that we start with the Base64 data (which could've also been received from a socket or some other communication line) and we load it in to a Buffer object. When creating the buffer we tell it that it's in base64 format, which allows the buffer to parse it accordingly for internal storage.

To save the data back in its raw PNG format, we simply pass the Buffer object to our fs.writeFileSync method and it does the conversion for us.

Conclusion

Base64 encoding is one of the most common ways of converting binary data into plain ASCII text. It is a very useful format for communicating between one or more systems that cannot easily handle binary data, like images in HTML markup or web requests.

In Node.js the Buffer object can be used to encode and decode Base64 strings to and from many other formats, allowing you to easily convert data back and forth as needed.

What do you typically use Base64 formatting for in Node.js? Let us know in the comments!