Decode Base64 ใน JavaScript: atob() และ Buffer
ใช้ ถอดรหัส Base64 ออนไลน์ ฟรีโดยตรงในเบราว์เซอร์ของคุณ — ไม่ต้องติดตั้ง
ลอง ถอดรหัส Base64 ออนไลน์ ออนไลน์ →เมื่อผมต้อง debug ปัญหาการยืนยันตัวตนบน production สิ่งแรกที่ผมหยิบมาใช้คือ ตัวถอดรหัส Base64 — JWT payload, webhook signature, และค่า config ที่เข้ารหัสไว้ ล้วนซ่อนอยู่ภายในสตริง Base64 JavaScript มีสองวิธีหลักในการ base64 decode: atob() (browser + Node.js 16+) และ Buffer.from(encoded, 'base64').toString() (Node.js) — และทั้งสองมีพฤติกรรมที่แตกต่างกันมากเมื่อข้อมูลต้นฉบับมีอักขระ Unicode สำหรับการ decode แบบครั้งเดียวโดยไม่ต้องเขียนโค้ด ตัวถอดรหัส Base64 ของ ToolDeck จัดการได้ทันทีในเบราว์เซอร์ของคุณ คู่มือนี้ครอบคลุมทั้งสองสภาพแวดล้อม — มุ่งเป้าไปที่ Node.js 16+ และเบราว์เซอร์สมัยใหม่ (Chrome 80+, Firefox 75+, Safari 14+) — พร้อม ตัวอย่างที่พร้อมใช้งานจริง: การกู้คืน UTF-8, ตัวแปร URL-safe, การ decode JWT, ไฟล์, การตอบสนอง API, Node.js streams, และสี่ข้อผิดพลาดที่มักสร้างผลลัพธ์ที่เสียหายใน codebase จริง
- ✓atob(encoded) พร้อมใช้งานแบบ native ในเบราว์เซอร์และใน Node.js 16+ แบบ global แต่คืนค่า binary string — ใช้ TextDecoder เพื่อกู้คืนข้อความ UTF-8 จากเนื้อหาใดก็ตามที่สูงกว่า ASCII
- ✓Buffer.from(encoded, "base64").toString("utf8") คือแนวทางมาตรฐานของ Node.js และจัดการ UTF-8 โดยอัตโนมัติโดยไม่ต้องมีขั้นตอนเพิ่มเติม
- ✓URL-safe Base64 (ใช้ใน JWT) แทน + ด้วย -, / ด้วย _, และตัด padding = ออก กู้คืนอักขระเหล่านี้ก่อนเรียก atob() หรือใช้ Buffer.from(encoded, "base64url").toString() ใน Node.js 18+
- ✓ลบ whitespace และ newline ก่อน decode — GitHub Contents API และ MIME encoder หลายตัวตัดบรรทัด Base64 output ที่ 60–76 อักขระต่อบรรทัด
- ✓Uint8Array.prototype.fromBase64() (TC39 Stage 3) พร้อมใช้งานแล้วใน Node.js 22+ และ Chrome 130+ และจะรวมทั้งสองสภาพแวดล้อมในที่สุด
Base64 Decoding คืออะไร?
Base64 decoding คือการย้อนกลับของ encoding — มันแปลงการแสดง ASCII 64 อักขระกลับ เป็นข้อมูลไบนารีหรือข้อความต้นฉบับ ทุก 4 อักขระ Base64 จะแมปกลับไปยัง 3 ไบต์ พอดี อักขระ padding = ที่ท้ายสตริงที่เข้ารหัสแจ้ง decoder ว่ามีกี่ไบต์เพิ่มเติมที่ถูกเพิ่มเข้าไปเพื่อ ให้กลุ่ม 3 ไบต์สุดท้ายสมบูรณ์
Base64 ไม่ใช่การเข้ารหัส — การดำเนินการนี้สามารถย้อนกลับได้อย่างสมบูรณ์โดยใครก็ตาม ที่มีสตริงที่เข้ารหัส จุดประสงค์คือความปลอดภัยในการส่ง: โปรโตคอลและรูปแบบการจัดเก็บ ที่ออกแบบมาสำหรับข้อความ ASCII 7 บิตไม่สามารถจัดการไบต์ไบนารีตามอำเภอใจได้ และ Base64 เชื่อมช่องว่างนั้น สถานการณ์การ decode JavaScript ทั่วไปได้แก่ การตรวจสอบ JWT payload, การแกะ JSON config ที่เข้ารหัส Base64 จากตัวแปรสภาพแวดล้อม, การแยก เนื้อหาไฟล์ไบนารีจาก REST API, และการ decode data URI ในเบราว์เซอร์
ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=
deploy-bot:sk-prod-a7f2c91e4b3d8
atob() — ฟังก์ชัน Decode แบบ Native ของเบราว์เซอร์
atob() (ASCII-to-binary) มีใน เบราว์เซอร์ตั้งแต่ IE10 และกลายเป็น global ใน Node.js 16.0 ซึ่งเป็นส่วนหนึ่งของ โครงการความเข้ากันได้ WinterCG มันยังทำงานแบบ native ใน Deno, Bun, และ Cloudflare Workers โดยไม่ต้อง import
ฟังก์ชันนี้คืนค่า binary string: สตริง JavaScript ที่แต่ละอักขระมี code point เท่ากับค่า byte ดิบหนึ่งค่า (0–255) สิ่งนี้สำคัญ: หากข้อมูลต้นฉบับเป็นข้อความ UTF-8 ที่มีอักขระสูงกว่า U+007F (ตัวอักษรที่มีเครื่องหมาย, Cyrillic, CJK, emoji), สตริงที่ คืนมาคือลำดับ byte ดิบ ไม่ใช่ข้อความที่อ่านได้ ใช้ TextDecoder เพื่อกู้คืนมัน (ครอบคลุมในส่วนถัดไป)
ตัวอย่างการทำงานขั้นต่ำ
// 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การตรวจสอบ round-trip
// 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
atob() และ btoa() เป็นส่วนหนึ่งของ WinterCG Minimum Common API — spec เดียวกับที่ควบคุม Fetch, URL, และ crypto ใน runtime ที่ไม่ใช่เบราว์เซอร์ ทั้งสองทำงานเหมือนกันใน Node.js 16+, Bun, Deno, และ Cloudflare Workersการกู้คืนข้อความ UTF-8 หลัง Decode
ปัญหาที่พบบ่อยที่สุดกับ atob() คือ การเข้าใจผิดเกี่ยวกับประเภทการคืนค่า เมื่อข้อความต้นฉบับถูกเข้ารหัสเป็น UTF-8 ก่อน Base64, atob() จะคืนค่า binary string แบบ Latin-1 ไม่ใช่ข้อความที่อ่านได้:
// 'Алексей Иванов' 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
วิธีที่ถูกต้องคือใช้ TextDecoder เพื่อตีความ byte ดิบเหล่านั้นเป็น UTF-8:
วิธี TextDecoder — ปลอดภัยสำหรับ output Unicode ทุกรูปแบบ
// 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: 250Buffer.from(encoded, 'base64').toString('utf8')มันตีความ byte ที่ถอดรหัสเป็น UTF-8 โดยอัตโนมัติและเร็วกว่าสำหรับ input ขนาดใหญ่Buffer.from() ใน Node.js — คู่มือ Decode ฉบับสมบูรณ์
ใน Node.js, Buffer คือ API มาตรฐาน สำหรับการดำเนินการไบนารีทั้งหมดรวมถึงการ decode Base64 มันจัดการ UTF-8 แบบ native, คืนค่า Buffer ที่แท้จริง (ปลอดภัยสำหรับ ไบนารี), และตั้งแต่ Node.js 18 รองรับ shortcut encoding 'base64url' สำหรับตัวแปร URL-safe
Decode config จากตัวแปรสภาพแวดล้อม
// 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กู้คืนไฟล์ไบนารีจากไฟล์ .b64
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 bytesDecode แบบ async พร้อมการจัดการ error
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`)ฟังก์ชัน Decode Base64 — อ้างอิงพารามิเตอร์
อ้างอิงด่วนสำหรับพารามิเตอร์ของ native decoding API หลักทั้งสอง จัดรูปแบบไว้สำหรับ ใช้ค้นหาเมื่อเขียนหรือรีวิวโค้ด
atob(encodedData)
| พารามิเตอร์ | ประเภท | จำเป็น | คำอธิบาย |
|---|---|---|---|
| encodedData | string | ใช่ | สตริง Base64 มาตรฐานที่ใช้อักขระ +, /, = ตัวแปร URL-safe (-, _) จะ throw InvalidCharacterError ไม่อนุญาต whitespace |
Buffer.from(input, inputEncoding) / .toString(outputEncoding)
| พารามิเตอร์ | ประเภท | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
| input | string | Buffer | TypedArray | ArrayBuffer | จำเป็น | สตริงที่เข้ารหัส Base64 ที่จะ decode หรือ buffer ที่มี byte ที่เข้ารหัส |
| inputEncoding | BufferEncoding | "utf8" | ตั้งเป็น "base64" สำหรับ Base64 มาตรฐาน (RFC 4648 §4) หรือ "base64url" สำหรับ URL-safe Base64 (RFC 4648 §5, Node.js 18+) |
| outputEncoding | string | "utf8" | Encoding สำหรับ output ของ .toString() ใช้ "utf8" สำหรับข้อความที่อ่านได้ "binary" สำหรับ binary string แบบ Latin-1 ที่เข้ากันได้กับ output ของ atob() |
| start | integer | 0 | Byte offset ภายใน Buffer ที่ decode แล้วเพื่อเริ่มอ่าน ส่งไปยัง .toString() เป็นอาร์กิวเมนต์ที่สอง |
| end | integer | buf.length | Byte offset ที่จะหยุดอ่าน (exclusive) ส่งไปยัง .toString() เป็นอาร์กิวเมนต์ที่สาม |
URL-safe Base64 — Decode JWT และพารามิเตอร์ URL
JWT ใช้ URL-safe Base64 (RFC 4648 §5) สำหรับทั้งสามส่วน URL-safe Base64 แทน + ด้วย - และ / ด้วย _, และตัด padding = ที่ท้ายออก การส่งตรงไปยัง atob() โดยไม่กู้คืนจะสร้าง output ที่ผิดหรือ throw error
Browser — กู้คืนอักขระและ padding ก่อน decode
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_3a7f91c2Node.js 18+ — encoding 'base64url' แบบ native
// 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_3a7f91c2Decode Base64 จากไฟล์และการตอบสนอง API
ในโค้ด production การ decode Base64 มักเกิดขึ้นมากที่สุดเมื่อใช้ API ภายนอกที่ส่ง เนื้อหาในรูปแบบที่เข้ารหัส ทั้งสองสถานการณ์มี gotcha สำคัญเกี่ยวกับ whitespace และ output ไบนารีเทียบกับข้อความ หากคุณแค่ต้องการตรวจสอบ response ที่เข้ารหัสระหว่าง debug ให้วางลงใน ตัวถอดรหัส Base64 โดยตรง — จัดการได้ทันทีทั้งโหมดมาตรฐานและ URL-safe
Decode เนื้อหาจาก GitHub Contents API
// 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}`)Decode ไบนารีที่เข้ารหัส Base64 จาก API (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')!)Decode Base64 ผ่าน Command-Line Node.js และ Shell
สำหรับ script CI/CD, เซสชัน debug, หรืองาน decode แบบครั้งเดียว เครื่องมือ shell และ one-liner ของ Node.js เร็วกว่า script เต็มรูปแบบ โปรดทราบว่าชื่อ flag แตกต่างกัน ระหว่าง macOS และ Linux
# ── 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"}base64 ใช้ -D เพื่อ decode (ตัวพิมพ์ใหญ่ D), ในขณะที่ Linux ใช้ -d (ตัวพิมพ์เล็ก) สิ่งนี้ทำให้ script CI เสียหายอย่างเงียบๆ — ใช้ one-liner ของ Node.js เมื่อไม่รับประกันว่าแพลตฟอร์มเป้าหมายเป็น Linuxทางเลือกประสิทธิภาพสูง: js-base64
เหตุผลหลักที่จะใช้ library คือความสอดคล้องข้ามสภาพแวดล้อม หากคุณส่ง package ที่ ทำงานในทั้ง browser และ Node.js โดยไม่มีการกำหนดค่า bundler, Buffer ต้องการการตรวจจับสภาพแวดล้อม และ atob() ต้องการ workaround TextDecoder js-base64 (100M+ ดาวน์โหลด npm ต่อสัปดาห์) จัดการทั้งสองอย่างโปร่งใส
npm install js-base64 # or pnpm add js-base64
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ผลลัพธ์ Terminal พร้อม Syntax Highlighting
เมื่อเขียนเครื่องมือ debug CLI หรือ script ตรวจสอบ, output console.log ธรรมดาอ่านยากสำหรับ JSON payload ขนาดใหญ่ chalk (package npm ที่ดาวน์โหลดมากที่สุดสำหรับการระบายสีใน terminal) รวมกับการ decode Base64 สร้าง output terminal ที่อ่านได้ง่ายและสแกนได้ — มีประโยชน์สำหรับการตรวจสอบ JWT, debug API response, และการตรวจสอบ config
npm install chalk # chalk v5+ is ESM-only — use import, not require
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: 1717203600Decode ไฟล์ Base64 ขนาดใหญ่ด้วย Node.js Streams
เมื่อไฟล์ที่เข้ารหัส Base64 เกิน ~50 MB การโหลดทั้งหมดเข้าหน่วยความจำด้วย readFileSync() กลายเป็นปัญหา Node.js streams ช่วยให้คุณ decode ข้อมูลเป็น chunk — แต่ Base64 ต้องการ 4 อักขระ คูณต่อ chunk (แต่ละกลุ่ม 4 อักขระ decode เป็น 3 ไบต์ พอดี) เพื่อหลีกเลี่ยง error padding ที่ขอบ chunk
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')4 × 1024 × 192 = 786,432 อักขระ (768 KB) สำหรับไฟล์ที่น้อยกว่า 50 MB, readFile() + Buffer.from(content.trim(), 'base64') ง่ายกว่าและเร็วพอข้อผิดพลาดที่พบบ่อย
ผมเห็นข้อผิดพลาดสี่ข้อนี้ใน JavaScript codebase ซ้ำๆ — มักซ่อนตัวอยู่จนกว่า อักขระที่ไม่ใช่ ASCII หรือ API response ที่มีการตัดบรรทัดจะถึงเส้นทาง decode ใน production
ข้อผิดพลาดที่ 1 — ใช้ atob() โดยไม่มี TextDecoder สำหรับเนื้อหา UTF-8
ปัญหา: atob() คืนค่า binary string ที่แต่ละ อักขระเป็นค่า byte ดิบหนึ่งค่า ลำดับ multi-byte UTF-8 (Cyrillic, CJK, อักขระที่มีเครื่องหมาย) ปรากฏเป็นอักขระ Latin-1 ที่อ่านไม่ได้ วิธีแก้: ห่อ output ใน TextDecoder
// ❌ 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) // Алексей Иванов ✓
ข้อผิดพลาดที่ 2 — ส่ง URL-safe Base64 โดยตรงไปยัง atob()
ปัญหา: ส่วนของ JWT ใช้ - และ _ แทน + และ /, โดยไม่มี padding atob() อาจคืนค่าข้อมูลที่ผิดหรือ throw วิธีแก้: กู้คืนอักขระมาตรฐานและเพิ่ม padding ก่อน
// ❌ 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"} ✓ข้อผิดพลาดที่ 3 — ไม่ลบ newline จาก Base64 ที่ถูกตัดบรรทัด
ปัญหา: GitHub Contents API และ MIME encoder ตัดบรรทัด Base64 output ที่ 60–76 อักขระ ต่อบรรทัด atob() throw InvalidCharacterError บนอักขระ \n วิธีแก้: ลบ whitespace ทั้งหมดก่อน decode
// ❌ 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) // ✓
ข้อผิดพลาดที่ 4 — เรียก .toString() บนเนื้อหาไบนารีที่ decode แล้ว
ปัญหา: เมื่อข้อมูลต้นฉบับเป็นไบนารี (รูปภาพ, PDF, เสียง), การเรียก .toString('utf8') จะแทนที่ ลำดับ byte ที่ไม่รู้จักด้วย U+FFFD, ทำให้ output เสียหายอย่างเงียบๆ วิธีแก้: เก็บผลลัพธ์เป็น Buffer — อย่าแปลงเป็น string
// ❌ .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วิธี Decode Base64 ใน JavaScript — เปรียบเทียบอย่างย่อ
| วิธี | Output UTF-8 | Output ไบนารี | URL-safe | สภาพแวดล้อม | ต้องติดตั้ง |
|---|---|---|---|---|---|
| atob() | ❌ ต้องใช้ TextDecoder | ✅ binary string | ❌ กู้คืนด้วยตนเอง | Browser, Node 16+, Bun, Deno | ไม่ |
| TextDecoder + atob() | ✅ UTF-8 | ✅ ผ่าน Uint8Array | ❌ กู้คืนด้วยตนเอง | Browser, Node 16+, Deno | ไม่ |
| Buffer.from().toString() | ✅ utf8 | ✅ เก็บเป็น Buffer | ✅ base64url (Node 18+) | Node.js, Bun | ไม่ |
| Uint8Array.fromBase64() (TC39) | ✅ ผ่าน TextDecoder | ✅ native | ✅ ตัวเลือก alphabet | Chrome 130+, Node 22+ | ไม่ |
| js-base64 | ✅ เสมอ | ✅ Uint8Array | ✅ ในตัว | Universal | npm install |
เลือก atob() เฉพาะเมื่อเนื้อหาที่ decode แล้วรับประกันว่าเป็นข้อความ ASCII ล้วน สำหรับข้อความหลายภาษาในเบราว์เซอร์ ให้ใช้ TextDecoder + atob() สำหรับโค้ด server-side ใน Node.js ให้ใช้ Buffer เป็นค่าเริ่มต้น — มันจัดการ UTF-8 โดยอัตโนมัติและเก็บข้อมูลไบนารีไว้ครบถ้วน สำหรับ library ข้ามสภาพแวดล้อม js-base64 ขจัด edge case ทั้งหมด
คำถามที่พบบ่อย
เครื่องมือที่เกี่ยวข้อง
สำหรับการ decode ด้วยคลิกเดียวโดยไม่ต้องเขียนโค้ด วาง Base64 string ของคุณลงใน ตัวถอดรหัส Base64 โดยตรง — จัดการทั้งโหมดมาตรฐานและ URL-safe พร้อม output ทันทีในเบราว์เซอร์ของคุณ
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.
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.