Copy of Community Portal Template_1.png

Sr. Developer Advocate

Appsmith

Do Base64 Strings Always End in an Equal Sign? - Working with Blobs, Binary and Base64

Introduction

If you've ever worked with data as a Base64 string, then you've probably noticed one or two = signs at the end, regardless of whether the encoded data is an image, document file, or just plain text. This encoded data can also be represented as a binary  (1's and 0's), or a blob (binary large object). Transferring file data across the web often requires converting the file before storing, or after retrieving it from the server. 

This post will break down the differences between Base64, Blobs, and Binary data, explore how to convert to and from each type using JavaScript. We'll also explore the difference between raw data and a file, and how to create a new file. 

Understanding Data Types

Binary Data

  • Binary data refers to the raw binary representation of content, typically in the form of arrays or buffers. It's used when you need direct access to binary data, such as processing binary files or performing low-level operations.

Blob (Binary Large Object)

  • Blobs are versatile data types for storing binary data such as images, videos, or files. They are commonly used when you need to handle large binary content or store it in databases. 

Base64 Encoding

  • Base64 encoding is a method to represent binary data in an ASCII format. It's widely used when transmitting binary data over text-based protocols or embedding images within HTML or CSS.

Fun Fact! "Blob" is a backronym

Aspect Base64 Encoding Blob Objects Binary Data
Format Text (ASCII) Generic data object Raw binary information
Usage Data transmission Storing binary data In-memory representation
Conversion Converts binary data to text Represents binary data Represents binary data
Example Use #1 Encoding images for HTML/CSS Storing files in the browser Raw sensor data handling (camera, mic)
Example Use #2 Transmitting data over text-based channels Creating/Editing files in JavaScript Working with low-level binary data

So what's up with those = signs? 

Base64 strings tend to end in one or two equal signs, but not always! These strings are not exact representations of the binary data in ASCII, because padding is applied to ensure the string length is a multiple of 4 characters. Each equal sign represents two bits of zero-padding. 

Ok, so why the padding? 

This basically comes down to compatibility. Not all data transmission channels can handle binary data gracefully. The padding gives the base64 string a predictable endpoint, so that the receiving server can more easily process the data. 

Data vs File

A file is like a container for data. It stores binary, blob, or base64 string in an object, along with the name, filetype, and metadata. 

Aspect File Data
Structure Organized, with name and metadata Raw and unstructured
Persistence Stored on permanent storage Typically in memory or variables
Temporality Persistent Often temporary
Representation External (e.g., documents, images) Internal (raw information)
Usage Accessed and manipulated by programs Used as input or output by programs

Working with Files in JavaScript

First let's look at how to get a file into the browser, so we have something to work with. There are a few different ways to achieve this. 

  1. From the end user, with a file upload dialog
  2. From an API or database query
  3. Building the file programmatically

Upload File from Client Side

To let the user upload an image from the client side and display it in the browser, all you need is a <input type=file> tag, and a function to run when the file is added. For displaying the file, we can use the URL.createObjectURL() method. 

<input type=file>
<img src=''></img>
<script>
var input = document.querySelector('input[type=file]')
input.addEventListener('change', onFileChange)
function onFileChange() {
 var file = input.files[0]
 document.querySelector('img').src = URL.createObjectURL(file);
}
</script>

Fetch a file from an API

Next we'll look at an example where the data comes from another server. For this we can use fetch() with an image url. 

<button onclick="fetchFileFromAPI()">Fetch File</button>
<script>
function fetchFileFromAPI() {
   fetch('https://www.appsmith.com/_next/image?url=%2Fassets%2Fimages%2Flogos%2FAppsmith_Logo.svg&w=128&q=75') // Replace with the actual API endpoint
       .then(response => response.blob()) // Assuming the response is a binary blob
       .then(blob => {
           const objectURL = URL.createObjectURL(blob);
           document.querySelector('img').src = objectURL;
       })
       .catch(error => {
           console.error('Error fetching file:', error);
       });
}
</script>
<img src="">

Building a File Programmatically

Files can also be created by defining a new Blob(),  and setting its contents and data type. 

<button onclick="createTextFile()">Create Text File</button>

<script>
function createTextFile() {
    // Define the content for the text file
    const fileContent = "This is a programmatically created text file.\nLine 2: Content goes here.";

    // Create a Blob with the text content
    const blob = new Blob([fileContent], { type: 'text/plain' });

    // Create a URL for the Blob
    const objectURL = URL.createObjectURL(blob);

    // Create a download link
    const downloadLink = document.createElement('a');
    downloadLink.innerHTML = "Download Text File";
    downloadLink.href = objectURL;

    // Set the file name
    downloadLink.download = "sample.txt";

    // Trigger a click event to initiate download
    downloadLink.click();
}
</script>

Converting Between Blob, Base64, and Binary Data 

Now lets look at how to convert between these formats. To convert a Blob we have to 'read' it using the FileReader() in JavaScript. 

Blob to Base64

This is useful for transforming binary data, such as images, into a format that can be easily embedded in data URLs or transmitted in text-based formats.

function blobToBase64(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result.split(',')[1]);
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
}

// Example usage:
const blob = /* Your Blob data */;
blobToBase64(blob).then(base64Data => {
  console.log(base64Data); // Base64 representation of the Blob
});

Base64 to Blob

This is commonly used when you need to convert Base64 data, like images, back into binary format for various data manipulation tasks, such as saving or displaying images. To convert back to a blob, we'll first use the atob() function to get the byteCharacters, then loop over the characters and change them back into binary using Unit8Array()

function base64ToBlob(base64String, mimeType) {
  const byteCharacters = atob(base64String);
  const byteNumbers = new Array(byteCharacters.length);

  for (let i = 0; i < byteCharacters.length; i++) {
    byteNumbers[i] = byteCharacters.charCodeAt(i);
  }

  const byteArray = new Uint8Array(byteNumbers);
  return new Blob([byteArray], { type: mimeType });
}

// Example usage:
const base64Data = /* Your Base64 data */;
const mimeType = 'image/png'; // Replace with the appropriate MIME type
const blob = base64ToBlob(base64Data, mimeType);

Blob to Binary Data (Typed Array)

This transformation is valuable when you need to work with binary data from Blobs, such as handling files or processing network responses. Since the data is a blob and not a file, we'll use the readAsArrayBuffer() method. 

function blobToBinary(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(new Uint8Array(reader.result));
    reader.onerror = reject;
    reader.readAsArrayBuffer(blob);
  });
}

// Example usage:
const blob = /* Your Blob data */;
blobToBinary(blob).then(binaryData => {
  console.log(binaryData); // Uint8Array representation of the Blob
});

Binary Data (Typed Array) to Blob

This is useful when you need to package binary data into a Blob, which is commonly used for handling and transmitting binary content in web applications.

function binaryToBlob(binaryData, mimeType) {
  return new Blob([binaryData], { type: mimeType });
}

// Example usage:
const binaryData = /* Your Binary data (Uint8Array) */;
const mimeType = 'application/octet-stream'; // Replace with the appropriate MIME type
const blob = binaryToBlob(binaryData, mimeType);

What About Data URLs? 

A Data URL is a scheme that allows data, typically in Base64 encoding, to be embedded directly into a web document, enabling inline representation of images, files, and other content. So instead of <img src="link_to_image.jpg" >, where the image is on a remote server, you can reference the base64 string directly, as the source. 

To create a data URL, just add data:{MIME_TYPE};base64, before the base64 string. 

<button id="showImage">Show Image</button>
<div id="imageContainer"></div>
<script>
  document.getElementById("showImage").addEventListener("click", function () {
    const base64String = "your_base64_data_here";
    const mimeType = "image/png";
    const dataURI = `data:${mimeType};base64,${base64String}`;
    const imgElement = document.createElement("img");
    imgElement.src = dataURI;
    document.getElementById("imageContainer").appendChild(imgElement);
  });
</script>

Conclusion

Working with data and files in JavaScript often requires converting between types before transferring, processing, or editing the data.