Decode Base64 di JavaScript: atob() & Buffer

ยทFront-end & Node.js DeveloperยทDitinjau olehSophie LaurentยทDiterbitkan

Gunakan Base64 Decode Online gratis langsung di browser Anda โ€” tidak perlu instalasi.

Coba Base64 Decode Online Online โ†’

Ketika saya men-debug masalah autentikasi di produksi, hal pertama yang saya ambil adalah Dekoder Base64 โ€” payload JWT, tanda tangan webhook, dan nilai konfigurasi yang dienkode semuanya tersembunyi di dalam string Base64. JavaScript menyediakan dua pendekatan bawaan utama untuk mendekode base64: atob() (browser + Node.js 16+) dan Buffer.from(encoded, 'base64').toString() (Node.js) โ€” dan keduanya berperilaku sangat berbeda ketika data asli mengandung karakter Unicode. Untuk dekode sekali pakai tanpa menulis kode apa pun, Dekoder Base64 ToolDeck menanganinya secara instan di browser Anda. Panduan ini mencakup kedua lingkungan โ€” menargetkan Node.js 16+ dan browser modern (Chrome 80+, Firefox 75+, Safari 14+) โ€” dengan contoh siap produksi: pemulihan UTF-8, varian URL-safe, dekoding JWT, file, respons API, Node.js streams, dan empat kesalahan yang secara konsisten menghasilkan output yang rusak di basis kode nyata.

  • โœ“atob(encoded) tersedia secara native di browser dan di Node.js 16+ secara global, tetapi mengembalikan binary string โ€” gunakan TextDecoder untuk memulihkan teks UTF-8 dari konten apa pun di atas ASCII.
  • โœ“Buffer.from(encoded, "base64").toString("utf8") adalah pendekatan idiomatis Node.js dan menangani UTF-8 secara otomatis tanpa langkah tambahan.
  • โœ“URL-safe Base64 (digunakan di JWT) mengganti + dengan -, / dengan _, dan menghilangkan padding =. Pulihkan karakter-karakter ini sebelum memanggil atob(), atau gunakan Buffer.from(encoded, "base64url").toString() di Node.js 18+.
  • โœ“Hapus spasi dan baris baru sebelum mendekode โ€” GitHub Contents API dan banyak encoder MIME membungkus output Base64 setiap 60โ€“76 karakter per baris.
  • โœ“Uint8Array.prototype.fromBase64() (TC39 Stage 3) sudah tersedia di Node.js 22+ dan Chrome 130+ dan akhirnya akan menyatukan kedua lingkungan.

Apa itu Dekoding Base64?

Dekoding Base64 adalah kebalikan dari encoding โ€” mengubah representasi ASCII 64-karakter kembali ke data biner atau teks asli. Setiap 4 karakter Base64 dipetakan kembali tepat ke 3 byte. Karakter padding = di akhir string yang dienkode memberi tahu dekoder berapa byte ekstra yang ditambahkan untuk melengkapi grup 3-byte terakhir.

Base64 bukan enkripsi โ€” operasinya sepenuhnya dapat dibalik oleh siapa saja yang memiliki string yang dienkode. Tujuannya adalah keamanan transport: protokol dan format penyimpanan yang dirancang untuk teks ASCII 7-bit tidak dapat menangani byte biner arbitrer, dan Base64 menjembatani kesenjangan tersebut. Skenario dekoding JavaScript yang umum meliputi inspeksi payload JWT, membongkar konfigurasi JSON yang dienkode Base64 dari variabel lingkungan, mengekstrak konten file biner dari REST API, dan mendekode data URI di browser.

Before ยท text
After ยท text
ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=
deploy-bot:sk-prod-a7f2c91e4b3d8

atob() โ€” Fungsi Dekoding Native Browser

atob() (ASCII-to-binary) telah tersedia di browser sejak IE10 dan menjadi global di Node.js 16.0 sebagai bagian dari inisiatif kompatibilitas WinterCG. Fungsi ini juga bekerja secara native di Deno, Bun, dan Cloudflare Workers โ€” tidak diperlukan import.

Fungsi ini mengembalikan binary string: string JavaScript di mana setiap karakter memiliki code point yang sama dengan satu nilai byte mentah (0โ€“255). Ini penting: jika data asli adalah teks UTF-8 yang mengandung karakter di atas U+007F (huruf beraksen, Cyrillic, CJK, emoji), string yang dikembalikan adalah urutan byte mentah, bukan teks yang dapat dibaca. Gunakan TextDecoder untuk memulihkannya (dibahas di bagian berikutnya).

Contoh kerja minimal

JavaScript (browser / Node.js 16+)
// Decoding an HTTP Basic Auth credential pair received in a request header
// Authorization: Basic ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=

function parseBasicAuth(header: string): { serviceId: string; apiKey: string } {
  const base64Part = header.replace(/^Basics+/i, '')
  const decoded    = atob(base64Part)
  const [serviceId, apiKey] = decoded.split(':')
  return { serviceId, apiKey }
}

const auth = parseBasicAuth('Basic ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=')

console.log(auth.serviceId) // deploy-bot
console.log(auth.apiKey)    // sk-prod-a7f2c91e4b3d8

Verifikasi round-trip

JavaScript
// Verify lossless recovery for ASCII-only content
const original = 'service:payments region:eu-west-1 env:production'

const encoded = btoa(original)
const decoded = atob(encoded)

console.log(encoded)
// c2VydmljZTpwYXltZW50cyByZWdpb246ZXUtd2VzdC0xIGVudjpwcm9kdWN0aW9u

console.log(decoded === original) // true
Catatan:atob() dan btoa() adalah bagian dari WinterCG Minimum Common API โ€” spesifikasi yang sama yang mengatur Fetch, URL, dan crypto di runtime non-browser. Keduanya berperilaku identik di Node.js 16+, Bun, Deno, dan Cloudflare Workers.

Memulihkan Teks UTF-8 Setelah Dekoding

Kesalahan paling umum dengan atob() adalah salah memahami tipe kembaliannya. Ketika teks asli dienkode sebagai UTF-8 sebelum Base64, atob() mengembalikan binary string Latin-1, bukan teks yang dapat dibaca:

JavaScript
// 'ะะปะตะบัะตะน ะ˜ะฒะฐะฝะพะฒ' was UTF-8 encoded then Base64 encoded before transmission
const encoded = '0JDQu9C10LrRgdC10Lkg0JjQstCw0L3QvtCy'

// โŒ atob() returns the raw UTF-8 bytes as a Latin-1 string โ€” garbled output
console.log(atob(encoded))
// "ะ ั’ะ ยปะ ยตะ ั”ะกะƒะ ยตะ โ„– ะ หœะ ะ†ะ ยฐะ ะ…ะ ั•ะ ะ†"  โ† byte values misread as Latin-1

Pendekatan yang benar menggunakan TextDecoder untuk menginterpretasikan byte mentah tersebut sebagai UTF-8:

Pendekatan TextDecoder โ€” aman untuk output Unicode apa pun

JavaScript (browser + Node.js 16+)
// Unicode-safe Base64 decode utilities
function fromBase64(encoded: string): string {
  const binary = atob(encoded)
  const bytes  = Uint8Array.from(binary, ch => ch.charCodeAt(0))
  return new TextDecoder().decode(bytes)
}

function toBase64(text: string): string {
  const bytes = new TextEncoder().encode(text)
  const chars = Array.from(bytes, byte => String.fromCharCode(byte))
  return btoa(chars.join(''))
}

// Works with any language or script
const orderNote = 'Confirmed: ็”ฐไธญๅคช้ƒŽ โ€” Sรฃo Paulo warehouse, qty: 250'
const encoded   = toBase64(orderNote)
const decoded   = fromBase64(encoded)

console.log(decoded === orderNote) // true
console.log(decoded)
// Confirmed: ็”ฐไธญๅคช้ƒŽ โ€” Sรฃo Paulo warehouse, qty: 250
Catatan:Di Node.js, lewati langkah TextDecoder sepenuhnya โ€” gunakan Buffer.from(encoded, 'base64').toString('utf8'). Fungsi ini menginterpretasikan byte yang didekode sebagai UTF-8 secara otomatis dan lebih cepat untuk input besar.

Buffer.from() di Node.js โ€” Panduan Lengkap Dekoding

Di Node.js, Buffer adalah API idiomatis untuk semua operasi biner termasuk dekoding Base64. Ia menangani UTF-8 secara native, mengembalikan Buffer yang tepat (aman biner), dan sejak Node.js 18 mendukung pintasan encoding 'base64url' untuk varian URL-safe.

Mendekode variabel lingkungan konfigurasi

Node.js
// Server config stored as Base64 in an env variable (avoids JSON escaping in shell)
// DB_CONFIG=eyJob3N0IjoiZGItcHJpbWFyeS5pbnRlcm5hbCIsInBvcnQiOjU0MzIsImRhdGFiYXNlIjoiYW5hbHl0aWNzX3Byb2QiLCJtYXhDb25uZWN0aW9ucyI6MTAwfQ==

const raw = Buffer.from(process.env.DB_CONFIG!, 'base64').toString('utf8')
const dbConfig = JSON.parse(raw)

console.log(dbConfig.host)           // db-primary.internal
console.log(dbConfig.port)           // 5432
console.log(dbConfig.maxConnections) // 100

Memulihkan file biner dari file .b64

Node.js
import { readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'

// Read the Base64-encoded certificate and restore the original binary
const encoded = readFileSync(join(process.cwd(), 'dist', 'cert.b64'), 'utf8').trim()
const certBuf  = Buffer.from(encoded, 'base64')

writeFileSync('./ssl/server.crt', certBuf)

console.log(`Restored ${certBuf.length} bytes`)
// Restored 2142 bytes

Dekoding async dengan penanganan error

Node.js
import { readFile, writeFile } from 'node:fs/promises'

async function decodeBase64File(
  encodedPath: string,
  outputPath:  string,
): Promise<number> {
  try {
    const encoded = await readFile(encodedPath, 'utf8')
    const binary  = Buffer.from(encoded.trim(), 'base64')
    await writeFile(outputPath, binary)
    return binary.length
  } catch (err) {
    const code = (err as NodeJS.ErrnoException).code
    if (code === 'ENOENT') throw new Error(`File not found: ${encodedPath}`)
    if (code === 'EACCES') throw new Error(`Permission denied: ${encodedPath}`)
    throw err
  }
}

// Restore a PDF stored as Base64
const bytes = await decodeBase64File('./uploads/invoice.b64', './out/invoice.pdf')
console.log(`Decoded ${bytes} bytes โ€” PDF restored`)

Fungsi Dekoding Base64 โ€” Referensi Parameter

Referensi cepat untuk parameter dari dua API dekoding native utama, diformat untuk digunakan sebagai acuan saat menulis atau meninjau kode.

atob(encodedData)

ParameterTipeDiperlukanDeskripsi
encodedDatastringYaString Base64 standar menggunakan karakter +, /, =. Varian URL-safe (-, _) melempar InvalidCharacterError. Spasi putih tidak diizinkan.
Mengembalikan: binary string โ€” code point setiap karakter sama dengan satu nilai byte mentah (0โ€“255). Bukan string Unicode; lewatkan melalui TextDecoder untuk memulihkan teks UTF-8.

Buffer.from(input, inputEncoding) / .toString(outputEncoding)

ParameterTipeDefaultDeskripsi
inputstring | Buffer | TypedArray | ArrayBufferdiperlukanString yang dienkode Base64 untuk didekode, atau buffer yang berisi byte yang dienkode.
inputEncodingBufferEncoding"utf8"Setel ke "base64" untuk Base64 standar (RFC 4648 ยง4), atau "base64url" untuk Base64 URL-safe (RFC 4648 ยง5, Node.js 18+).
outputEncodingstring"utf8"Encoding untuk output .toString(). Gunakan "utf8" untuk teks yang dapat dibaca, "binary" untuk binary string Latin-1 yang kompatibel dengan output atob().
startinteger0Offset byte dalam Buffer yang didekode untuk mulai membaca. Dilewatkan ke .toString() sebagai argumen kedua.
endintegerbuf.lengthOffset byte untuk berhenti membaca (eksklusif). Dilewatkan ke .toString() sebagai argumen ketiga.
Mengembalikan: Buffer dari .from(). Mengembalikan string dari .toString(). Pertahankan sebagai Buffer (jangan panggil .toString()) ketika konten yang didekode bersifat biner โ€” gambar, PDF, audio.

URL-safe Base64 โ€” Mendekode JWT dan Parameter URL

JWT menggunakan URL-safe Base64 (RFC 4648 ยง5) untuk ketiga segmennya. URL-safe Base64 mengganti + dengan - dan / dengan _, dan menghilangkan padding = di belakang. Meneruskan ini langsung ke atob() tanpa pemulihan menghasilkan output yang salah atau melempar error.

Browser โ€” pulihkan karakter dan padding sebelum mendekode

JavaScript (browser)
function decodeBase64Url(input: string): string {
  const base64 = input.replace(/-/g, '+').replace(/_/g, '/')
  const padded = base64 + '==='.slice(0, (4 - base64.length % 4) % 4)
  const binary = atob(padded)
  const bytes  = Uint8Array.from(binary, ch => ch.charCodeAt(0))
  return new TextDecoder().decode(bytes)
}

// Inspect a JWT payload segment (the middle part between the two dots)
const jwtToken  = 'eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciIsIndvcmtzcGFjZUlkIjoid3NfM2E3ZjkxYzIiLCJleHAiOjE3MTcyMDM2MDB9'
const payload   = decodeBase64Url(jwtToken)
const claims    = JSON.parse(payload)

console.log(claims.userId)      // usr_9f2a1c3e8b4d
console.log(claims.role)        // editor
console.log(claims.workspaceId) // ws_3a7f91c2

Node.js 18+ โ€” encoding 'base64url' native

Node.js 18+
// Node.js 18 added 'base64url' as a first-class Buffer encoding โ€” no manual replace needed
function decodeJwtSegment(segment: string): Record<string, unknown> {
  const json = Buffer.from(segment, 'base64url').toString('utf8')
  return JSON.parse(json)
}

const token   = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciIsIndvcmtzcGFjZUlkIjoid3NfM2E3ZjkxYzIiLCJleHAiOjE3MTcyMDM2MDB9.SIGNATURE'
const [headerB64, payloadB64] = token.split('.')

const header  = decodeJwtSegment(headerB64)
const payload = decodeJwtSegment(payloadB64)

console.log(header.alg)          // HS256
console.log(payload.role)        // editor
console.log(payload.workspaceId) // ws_3a7f91c2

Mendekode Base64 dari File dan Respons API

Dalam kode produksi, dekoding Base64 paling sering terjadi saat mengonsumsi API eksternal yang mengirimkan konten dalam bentuk yang dienkode. Kedua skenario memiliki gotcha penting seputar spasi putih dan output biner vs teks. Jika Anda hanya perlu memeriksa respons yang dienkode saat debugging, tempel langsung ke Dekoder Base64 โ€” menangani mode standar dan URL-safe secara instan.

Mendekode konten dari GitHub Contents API

JavaScript
// GitHub Contents API returns file content as Base64, wrapped at 60 chars per line
async function fetchDecodedFile(
  owner: string,
  repo:  string,
  path:  string,
  token: string,
): Promise<string> {
  const res = await fetch(
    `https://api.github.com/repos/${owner}/${repo}/contents/${path}`,
    { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github.v3+json' } }
  )
  if (!res.ok) throw new Error(`GitHub API ${res.status}: ${res.statusText}`)

  const data = await res.json() as { content: string; encoding: string }
  if (data.encoding !== 'base64') throw new Error(`Unexpected encoding: ${data.encoding}`)

  // โš ๏ธ GitHub wraps at 60 chars โ€” strip newlines before decoding
  const clean = data.content.replace(/\n/g, '')
  return Buffer.from(clean, 'base64').toString('utf8')
}

const openApiSpec = await fetchDecodedFile('acme-corp', 'platform-api', 'openapi.json', process.env.GITHUB_TOKEN!)
const spec = JSON.parse(openApiSpec)
console.log(`API version: ${spec.info.version}`)

Mendekode biner yang dienkode Base64 dari API (browser)

JavaScript (browser)
// Some APIs return binary content (images, PDFs) as Base64 JSON fields
async function downloadDecodedFile(endpoint: string, authToken: string): Promise<void> {
  const res = await fetch(endpoint, { headers: { Authorization: `Bearer ${authToken}` } })
  if (!res.ok) throw new Error(`Download failed: ${res.status}`)

  const { filename, content, mimeType } = await res.json() as {
    filename: string; content: string; mimeType: string
  }

  // Decode Base64 โ†’ binary bytes โ†’ Blob
  const binary = atob(content)
  const bytes  = Uint8Array.from(binary, ch => ch.charCodeAt(0))
  const blob   = new Blob([bytes], { type: mimeType })

  // Trigger browser download
  const url = URL.createObjectURL(blob)
  const a   = Object.assign(document.createElement('a'), { href: url, download: filename })
  a.click()
  URL.revokeObjectURL(url)
}

await downloadDecodedFile('/api/reports/latest', sessionStorage.getItem('auth_token')!)

Dekoding Base64 di Command-Line Node.js dan Shell

Untuk skrip CI/CD, sesi debugging, atau tugas dekoding sekali pakai, alat shell dan one-liner Node.js lebih cepat daripada skrip penuh. Perhatikan bahwa nama flag berbeda antara macOS dan Linux.

bash
# โ”€โ”€ macOS / Linux system base64 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Standard decoding (macOS uses -D, Linux uses -d)
echo "ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=" | base64 -d   # Linux
echo "ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=" | base64 -D   # macOS

# Decode a .b64 file to its original binary
base64 -d ./dist/cert.b64 > ./ssl/server.crt       # Linux
base64 -D -i ./dist/cert.b64 -o ./ssl/server.crt   # macOS

# URL-safe Base64 โ€” restore + and / before decoding
echo "eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ" | tr '-_' '+/' | base64 -d

# โ”€โ”€ Node.js one-liner โ€” works on Windows too โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
node -e "process.stdout.write(Buffer.from(process.argv[1], 'base64').toString())" "ZGVwbG95LWJvdA=="
# deploy-bot

# URL-safe (Node.js 18+)
node -e "process.stdout.write(Buffer.from(process.argv[1], 'base64url').toString())" "eyJhbGciOiJIUzI1NiJ9"
# {"alg":"HS256"}
Catatan:Di macOS, base64 menggunakan -D untuk mendekode (huruf D besar), sedangkan Linux menggunakan -d (huruf kecil). Ini merusak skrip CI secara diam-diam โ€” gunakan one-liner Node.js ketika platform target tidak dijamin Linux.

Alternatif Berkinerja Tinggi: js-base64

Alasan utama untuk menggunakan library adalah konsistensi lintas lingkungan. Jika Anda mengirim paket yang berjalan di browser dan Node.js tanpa konfigurasi bundler, Buffer memerlukan deteksi lingkungan dan atob() memerlukan solusi TextDecoder. js-base64 (100M+ unduhan npm mingguan) menangani keduanya secara transparan.

bash
npm install js-base64
# or
pnpm add js-base64
JavaScript
import { fromBase64, fromBase64Url, isValid } from 'js-base64'

// Standard decoding โ€” Unicode-safe, works in browser and Node.js
const raw   = fromBase64('eyJldmVudElkIjoiZXZ0XzdjM2E5ZjFiMmQiLCJ0eXBlIjoiY2hlY2tvdXRfY29tcGxldGVkIiwiY3VycmVuY3kiOiJFVVIiLCJhbW91bnQiOjE0OTAwfQ==')
const event = JSON.parse(raw)
console.log(event.type)     // checkout_completed
console.log(event.currency) // EUR

// URL-safe decoding โ€” no manual character replacement needed
const jwtPayload = fromBase64Url('eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciJ9')
const claims     = JSON.parse(jwtPayload)
console.log(claims.role) // editor

// Validate before decoding untrusted input
const untrusted = 'not!valid@base64#'
if (!isValid(untrusted)) {
  console.error('Rejected: invalid Base64 input')
}

// Binary output โ€” second argument true returns Uint8Array
const pngBytes = fromBase64('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==', true)
console.log(pngBytes instanceof Uint8Array) // true

Output Terminal dengan Syntax Highlighting

Saat menulis alat debugging CLI atau skrip inspeksi, output console.log biasa sulit dibaca untuk payload JSON yang besar. chalk (paket npm paling banyak diunduh untuk pewarnaan terminal) dikombinasikan dengan dekoding Base64 menghasilkan output terminal yang mudah dibaca dan dapat dipindai โ€” berguna untuk inspeksi JWT, debugging respons API, dan audit konfigurasi.

bash
npm install chalk
# chalk v5+ is ESM-only โ€” use import, not require
Node.js
import chalk from 'chalk'

// Decode and display any Base64 value with smart type detection
function inspectBase64(encoded: string, label = 'Decoded value'): void {
  let decoded: string
  try {
    decoded = Buffer.from(encoded.trim(), 'base64').toString('utf8')
  } catch {
    console.error(chalk.red('โœ— Invalid Base64 input'))
    return
  }

  console.log(chalk.bold.cyan(`\nโ”€โ”€ ${label} โ”€โ”€`))

  // Attempt JSON pretty-print
  try {
    const parsed = JSON.parse(decoded)
    console.log(chalk.green('Type:'), chalk.yellow('JSON'))
    for (const [key, value] of Object.entries(parsed)) {
      const display = typeof value === 'object' ? JSON.stringify(value) : String(value)
      console.log(chalk.green(`  ${key}:`), chalk.white(display))
    }
    return
  } catch { /* not JSON */ }

  // Plain text fallback
  console.log(chalk.green('Type:'), chalk.yellow('text'))
  console.log(chalk.white(decoded))
}

// Inspect a Base64-encoded JWT payload
const tokenParts = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciIsImV4cCI6MTcxNzIwMzYwMH0.SIGNATURE'.split('.')
inspectBase64(tokenParts[0], 'JWT Header')
inspectBase64(tokenParts[1], 'JWT Payload')
// โ”€โ”€ JWT Header โ”€โ”€
// Type:   JSON
//   alg:  HS256
//   typ:  JWT
//
// โ”€โ”€ JWT Payload โ”€โ”€
// Type:   JSON
//   userId: usr_9f2a1c3e8b4d
//   role:   editor
//   exp:    1717203600
Catatan:Gunakan chalk hanya untuk output terminal/CLI โ€” jangan pernah untuk konten yang ditulis ke file, respons API, atau log aggregator. Kode escape ANSI merusak konsumen non-terminal: platform log (Datadog, Splunk), parser log JSON, dan viewer log CI semuanya menampilkannya sebagai urutan karakter yang tidak terbaca.

Dekode File Base64 Besar dengan Node.js Streams

Ketika file yang dienkode Base64 melebihi ~50 MB, memuatnya seluruhnya ke memori dengan readFileSync() menjadi masalah. Node.js streams memungkinkan Anda mendekode data dalam potongan โ€” tetapi Base64 memerlukan kelipatan 4 karakter per potongan (setiap grup 4-karakter mendekode tepat 3 byte) untuk menghindari error padding di batas potongan.

Node.js
import { createReadStream, createWriteStream } from 'node:fs'
import { pipeline } from 'node:stream/promises'

async function streamDecodeBase64(inputPath: string, outputPath: string): Promise<void> {
  const readStream  = createReadStream(inputPath, { encoding: 'utf8', highWaterMark: 4 * 1024 * 192 })
  const writeStream = createWriteStream(outputPath)

  let buffer = ''

  await pipeline(
    readStream,
    async function* (source) {
      for await (const chunk of source) {
        buffer += (chunk as string).replace(/\s/g, '') // strip any whitespace/newlines

        // Decode only complete 4-char groups to avoid mid-stream padding issues
        const remainder = buffer.length % 4
        const safe      = buffer.slice(0, buffer.length - remainder)
        buffer          = buffer.slice(buffer.length - remainder)

        if (safe.length > 0) yield Buffer.from(safe, 'base64')
      }
      if (buffer.length > 0) yield Buffer.from(buffer, 'base64')
    },
    writeStream,
  )
}

// Decode a 200 MB video that was stored as Base64
await streamDecodeBase64('./uploads/product-demo.b64', './dist/product-demo.mp4')
console.log('Stream decode complete')
Catatan:Ukuran potongan harus kelipatan 4 karakter saat membaca teks Base64, sehingga setiap potongan hanya berisi grup 4-karakter yang lengkap. Contoh menggunakan 4 ร— 1024 ร— 192 = 786.432 karakter (768 KB). Untuk file di bawah 50 MB, readFile() + Buffer.from(content.trim(), 'base64') lebih sederhana dan cukup cepat.

Kesalahan Umum

Saya telah melihat empat kesalahan ini berulang kali dalam basis kode JavaScript โ€” kesalahan ini cenderung tersembunyi hingga karakter non-ASCII atau respons API dengan baris yang dibungkus mencapai jalur dekoding di produksi.

Kesalahan 1 โ€” Menggunakan atob() tanpa TextDecoder untuk konten UTF-8

Masalah: atob() mengembalikan binary string di mana setiap karakter adalah satu nilai byte mentah. Urutan multi-byte UTF-8 (Cyrillic, CJK, karakter beraksen) muncul sebagai karakter Latin-1 yang rusak. Perbaikan: bungkus output dalam TextDecoder.

Before ยท JavaScript
After ยท JavaScript
// โŒ atob() returns the raw UTF-8 bytes as a Latin-1 string
const encoded = '0JDQu9C10LrRgdC10Lkg0JjQstCw0L3QvtCy'
const decoded  = atob(encoded)
console.log(decoded)
// "ะ ั’ะ ยปะ ยตะ ั”ะกะƒะ ยตะ โ„– ะ หœะ ะ†ะ ยฐะ ะ…ะ ั•ะ ะ†"  โ† wrong
// โœ… Use TextDecoder to correctly interpret the UTF-8 bytes
const encoded  = '0JDQu9C10LrRgdC10Lkg0JjQstCw0L3QvtCy'
const binary   = atob(encoded)
const bytes    = Uint8Array.from(binary, ch => ch.charCodeAt(0))
const decoded  = new TextDecoder().decode(bytes)
console.log(decoded) // ะะปะตะบัะตะน ะ˜ะฒะฐะฝะพะฒ โœ“

Kesalahan 2 โ€” Meneruskan URL-safe Base64 langsung ke atob()

Masalah: Segmen JWT menggunakan - dan _ sebagai pengganti + dan /, tanpa padding. atob() mungkin mengembalikan data yang salah atau melempar error. Perbaikan: pulihkan karakter standar dan tambahkan kembali padding terlebih dahulu.

Before ยท JavaScript
After ยท JavaScript
// โŒ URL-safe JWT segment passed directly โ€” unreliable
const jwtPayload = 'eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ'
const decoded    = atob(jwtPayload) // May produce wrong result or throw
// โœ… Restore standard Base64 chars and padding first
function decodeBase64Url(input: string): string {
  const b64  = input.replace(/-/g, '+').replace(/_/g, '/')
  const pad  = b64 + '==='.slice(0, (4 - b64.length % 4) % 4)
  const bin  = atob(pad)
  const bytes = Uint8Array.from(bin, ch => ch.charCodeAt(0))
  return new TextDecoder().decode(bytes)
}
const decoded = decodeBase64Url('eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ')
// {"userId":"usr_9f2a1c3e"} โœ“

Kesalahan 3 โ€” Tidak menghapus baris baru dari Base64 yang dibungkus

Masalah: GitHub Contents API dan encoder MIME membungkus output Base64 setiap 60โ€“76 karakter per baris. atob() melempar InvalidCharacterError pada karakter \n. Perbaikan: hapus semua spasi putih sebelum mendekode.

Before ยท JavaScript
After ยท JavaScript
// โŒ GitHub API content field contains embedded newlines
const data    = await res.json()
const decoded = atob(data.content) // โŒ throws InvalidCharacterError
// โœ… Strip newlines (and any other whitespace) before decoding
const data    = await res.json()
const clean   = data.content.replace(/\s/g, '')
const decoded = atob(clean) // โœ“

Kesalahan 4 โ€” Memanggil .toString() pada konten biner yang didekode

Masalah: Ketika data asli bersifat biner (gambar, PDF, audio), memanggil .toString('utf8') mengganti urutan byte yang tidak dikenali dengan U+FFFD, secara diam-diam merusak output. Perbaikan: pertahankan hasilnya sebagai Buffer โ€” jangan konversi ke string.

Before ยท JavaScript
After ยท JavaScript
// โŒ .toString('utf8') corrupts binary content
import { readFileSync, writeFileSync } from 'node:fs'
const encoded   = readFileSync('./uploads/invoice.b64', 'utf8').trim()
const corrupted = Buffer.from(encoded, 'base64').toString('utf8') // โŒ
writeFileSync('./out/invoice.pdf', corrupted) // โŒ unreadable PDF
// โœ… Keep the Buffer as binary โ€” do not convert to a string
import { readFileSync, writeFileSync } from 'node:fs'
const encoded = readFileSync('./uploads/invoice.b64', 'utf8').trim()
const binary  = Buffer.from(encoded, 'base64') // โœ“ raw bytes preserved
writeFileSync('./out/invoice.pdf', binary)      // โœ“ valid PDF

Metode Dekoding Base64 JavaScript โ€” Perbandingan Cepat

MetodeOutput UTF-8Output BinerURL-safeLingkunganPerlu instalasi
atob()โŒ perlu TextDecoderโœ… binary stringโŒ pulihkan manualBrowser, Node 16+, Bun, DenoTidak
TextDecoder + atob()โœ… UTF-8โœ… via Uint8ArrayโŒ pulihkan manualBrowser, Node 16+, DenoTidak
Buffer.from().toString()โœ… utf8โœ… simpan sebagai Bufferโœ… base64url (Node 18+)Node.js, BunTidak
Uint8Array.fromBase64() (TC39)โœ… via TextDecoderโœ… nativeโœ… opsi alphabetChrome 130+, Node 22+Tidak
js-base64โœ… selaluโœ… Uint8Arrayโœ… bawaanUniversalnpm install

Pilih atob() hanya ketika konten yang didekode dijamin berupa teks ASCII. Untuk teks yang disediakan pengguna atau multi-bahasa di browser, gunakan TextDecoder + atob(). Untuk kode server-side Node.js, Buffer adalah default yang tepat โ€” ia menangani UTF-8 secara otomatis dan menjaga data biner tetap utuh. Untuk library lintas lingkungan, js-base64 menghilangkan semua edge case.

Pertanyaan yang Sering Diajukan

Mengapa atob() mengembalikan karakter yang rusak alih-alih teks yang dapat dibaca?
atob() mengembalikan binary string di mana setiap karakter mewakili satu byte mentah (0โ€“255), bukan code point Unicode. Jika teks asli dienkode sebagai UTF-8, karakter apa pun di atas U+007F โ€” Cyrillic, Arab, ideograf CJK, huruf beraksen โ€” akan muncul sebagai dua atau lebih karakter Latin-1 yang rusak. Perbaikannya: lewatkan output melalui TextDecoder: const bytes = Uint8Array.from(atob(encoded), ch => ch.charCodeAt(0)); const text = new TextDecoder().decode(bytes). Di Node.js, gunakan Buffer.from(encoded, 'base64').toString('utf8') yang menangani ini secara otomatis.
Bagaimana cara mendekode payload token JWT di JavaScript?
JWT memiliki tiga segmen URL-safe Base64 yang dipisahkan oleh titik: header.payload.signature. Untuk mendekode payload: const [, payloadB64] = token.split('.'). Di browser: pulihkan karakter standar, tambahkan padding, dekode dengan atob() dan TextDecoder. Di Node.js 18+: Buffer.from(payloadB64, 'base64url').toString('utf8'). Penting: dekoding hanya mengungkapkan klaim โ€” ini TIDAK memverifikasi tanda tangan. Gunakan library JWT yang tepat (jsonwebtoken, jose) untuk dekoding yang diverifikasi di produksi.
Apa perbedaan antara atob() dan Buffer.from() untuk dekoding?
atob() tersedia di semua lingkungan JavaScript (browser, Node.js 16+, Bun, Deno) tanpa import, tetapi mengembalikan binary string โ€” Anda memerlukan TextDecoder untuk mengkonversi konten UTF-8 ke teks yang dapat dibaca. Buffer.from(encoded, 'base64') hanya untuk Node.js / Bun, mengembalikan Buffer yang sebenarnya (aman biner), secara native menangani UTF-8, dan mendukung 'base64url' di Node.js 18+. Untuk kode server-side, Buffer lebih sederhana. Untuk kode browser, atob() + TextDecoder adalah standar. Untuk library lintas lingkungan, js-base64 mengabstraksikan perbedaannya.
Bagaimana cara mendekode URL-safe Base64 di browser?
URL-safe Base64 mengganti + dengan -, / dengan _, dan menghilangkan padding =. Pulihkan dulu sebelum memanggil atob(): const b64 = input.replace(/-/g, '+').replace(/_/g, '/'); const padded = b64 + '==='.slice(0, (4 - b64.length % 4) % 4); const text = new TextDecoder().decode(Uint8Array.from(atob(padded), c => c.charCodeAt(0))). Di Node.js 18+: Buffer.from(input, 'base64url').toString('utf8') menanganinya dalam satu panggilan.
Bagaimana cara mendekode konten Base64 dari GitHub API di JavaScript?
GitHub Contents API mengembalikan konten file sebagai Base64 standar dengan karakter baris baru setiap 60 karakter. Hapus sebelum mendekode: const clean = data.content.replace(/\n/g, ''). Di browser: new TextDecoder().decode(Uint8Array.from(atob(clean), c => c.charCodeAt(0))). Di Node.js: Buffer.from(clean, 'base64').toString('utf8'). Untuk file biner (gambar, PDF), pertahankan Buffer tanpa memanggil .toString() โ€” teruskan langsung ke writeFile atau stream respons.
Bisakah saya mendekode gambar yang dienkode Base64 di JavaScript tanpa library?
Ya. Di browser: const binary = atob(encoded); const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0)); const blob = new Blob([bytes], { type: 'image/png' }); const url = URL.createObjectURL(blob). Untuk src img, buat data URI sebagai gantinya: const src = 'data:image/png;base64,' + encoded โ€” ini melewati langkah dekode sepenuhnya. Di Node.js: Buffer.from(encoded, 'base64') diikuti dengan writeFileSync('./out.png', buffer). Aturan utama: jangan pernah memanggil .toString() pada Buffer yang didekode ketika kontennya biner.

Alat Terkait

Untuk dekode sekali klik tanpa menulis kode apa pun, tempel string Base64 Anda langsung ke Dekoder Base64 โ€” menangani mode standar dan URL-safe dengan output langsung di browser Anda.

Tersedia juga dalam:PythonGoJavaC#
AC
Alex ChenFront-end & Node.js Developer

Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.

SL
Sophie LaurentPeninjau teknis

Sophie is a full-stack developer focused on TypeScript across the entire stack โ€” from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.