Encoding Binary Data with Base64

Learn how to Base64 encode raw binary data like images, PDFs, and executables. Understand the byte-level process, typed arrays, and streaming approaches.

Encoding

Detailed Explanation

Base64 was designed specifically for encoding binary data as text. While it is commonly demonstrated with string inputs, its primary purpose is handling raw bytes that cannot be directly represented in text-based formats like JSON, XML, or email.

What counts as binary data?

Any data that is not plain text: images (PNG, JPEG, WebP), documents (PDF, DOCX), archives (ZIP, TAR), audio (MP3, WAV), video (MP4), executables, serialized objects, cryptographic keys, and any file that contains byte values across the full 0-255 range.

Working with binary data in JavaScript:

The browser provides typed arrays for binary manipulation:

// Create binary data
const bytes = new Uint8Array([0x89, 0x50, 0x4E, 0x47]); // PNG magic bytes

// Encode to Base64
const binaryString = Array.from(bytes, b => String.fromCharCode(b)).join("");
const base64 = btoa(binaryString);

// Decode from Base64
const decoded = atob(base64);
const decodedBytes = Uint8Array.from(decoded, c => c.charCodeAt(0));

Reading files as binary in the browser:

const input = document.querySelector('input[type="file"]');
input.addEventListener("change", async () => {
  const file = input.files[0];
  const arrayBuffer = await file.arrayBuffer();
  const bytes = new Uint8Array(arrayBuffer);

  // Convert to Base64
  let binary = "";
  for (let i = 0; i < bytes.length; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  const base64 = btoa(binary);
});

Chunked encoding for large files:

For files larger than a few megabytes, encoding in one shot can freeze the browser or cause out-of-memory errors. A chunked approach processes the data in pieces:

async function encodeFileChunked(file, chunkSize = 1024 * 1024) {
  const reader = file.stream().getReader();
  const chunks = [];
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    chunks.push(value);
  }
  // Combine and encode (simplified)
  const combined = new Uint8Array(chunks.reduce((a, c) => a + c.length, 0));
  let offset = 0;
  for (const chunk of chunks) {
    combined.set(chunk, offset);
    offset += chunk.length;
  }
  return btoa(String.fromCharCode(...combined));
}

In Node.js, Buffer handles everything:

const fs = require("fs");
const fileBuffer = fs.readFileSync("data.bin");
const base64 = fileBuffer.toString("base64");
const restored = Buffer.from(base64, "base64");

Key insight: Unlike text encoding where you worry about character sets and encodings (UTF-8, Latin-1), binary-to-Base64 is straightforward because you are working directly with bytes. There is no character encoding step -- the raw bytes go directly into the Base64 algorithm.

Use Case

Transmitting a client-generated cryptographic key pair through a WebSocket connection that only supports text frames, using Base64 to serialize the raw key bytes.

Try It — Base64 Encoder

Open full tool