Base64 JavaScript — btoa() & Buffer
Sử dụng Bộ Mã Hóa Base64 Trực Tuyến miễn phí trực tiếp trên trình duyệt — không cần cài đặt.
Dùng thử Bộ Mã Hóa Base64 Trực Tuyến trực tuyến →Khi bạn nhúng ảnh vào CSS data URI, truyền thông tin xác thực trong header HTTP Authorization, hoặc lưu trữ chứng chỉ nhị phân trong biến môi trường, bạn cần mã hóa Base64 dữ liệu JavaScript một cách đáng tin cậy trên cả trình duyệt và Node.js. JavaScript cung cấp hai API tích hợp riêng biệt:btoa() cho môi trường trình duyệt (cũng có trong Node.js 16+) và Buffer.from() cho Node.js — mỗi cái có ràng buộc khác nhau về Unicode, dữ liệu nhị phân và độ an toàn URL. Để mã hóa nhanh mà không cần viết code, Base64 Encoder của ToolDeck xử lý ngay lập tức trong trình duyệt. Hướng dẫn này bao gồm cả hai môi trường với các ví dụ sẵn sàng cho production: xử lý Unicode, biến thể URL-safe, mã hóa tệp và phản hồi API, sử dụng CLI, và bốn lỗi thường xuyên gây ra bug trong codebase thực tế.
- ✓btoa() có sẵn native trong trình duyệt và toàn cục trong Node.js 16+, nhưng chỉ chấp nhận Latin-1 (code point 0–255) — input Unicode sẽ ném ra DOMException
- ✓Buffer.from(text, "utf8").toString("base64") là phương thức tương đương trong Node.js, xử lý Unicode natively không cần bước bổ sung
- ✓Base64 URL-safe thay thế + → -, / → _, và bỏ padding = — dùng Buffer.from().toString("base64url") trong Node.js 18+ để có one-liner
- ✓Với dữ liệu nhị phân (ArrayBuffer, Uint8Array, tệp), dùng Buffer trong Node.js hoặc cách arrayBuffer() + Uint8Array trong trình duyệt — không bao giờ dùng response.text()
- ✓Uint8Array.prototype.toBase64() (TC39 Stage 3) đã có trong Node.js 22+ và Chrome 130+, sẽ thống nhất cả hai môi trường
Base64 Encoding là gì?
Base64 chuyển đổi dữ liệu nhị phân tùy ý thành chuỗi được xây dựng từ 64 ký tự ASCII có thể in: A–Z, a–z, 0–9, +, và /. Mỗi 3 byte đầu vào ánh xạ chính xác đến 4 ký tự Base64; nếu độ dài đầu vào không phải là bội số của 3, một hoặc hai ký tự padding = được thêm vào. Đầu ra được mã hóa luôn lớn hơn bản gốc khoảng 33%.
Base64 không phải là mã hóa — nó không cung cấp tính bảo mật. Bất kỳ ai có chuỗi đã mã hóa đều có thể giải mã nó bằng một lần gọi hàm. Mục đích của nó là an toàn truyền tải: nhiều giao thức và định dạng lưu trữ được thiết kế cho văn bản ASCII 7-bit và không thể xử lý byte nhị phân tùy ý. Base64 lấp đầy khoảng cách đó. Các trường hợp sử dụng JavaScript phổ biến bao gồm data URI để nhúng tài sản, header HTTP Basic Auth, các phân đoạn token JWT, tệp đính kèm email MIME, và lưu trữ blob nhị phân trong JSON API.
deploy-bot:sk-prod-a7f2c91e4b3d8
ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=
btoa() — Hàm Mã hóa Native của Trình duyệt
btoa() (binary-to-ASCII) đã có trong trình duyệt từ IE10 và trở thành global trong Node.js 16.0 như một phần của sáng kiến tương thích WinterCG. Nó cũng hoạt động natively trong Deno, Bun, và Cloudflare Workers. Không cần import.
Hàm nhận một đối số chuỗi và trả về dạng được mã hóa Base64. Hàm đối xứng atob() (ASCII-to-binary) giải mã trở lại. Cả hai đều đồng bộ và chạy với bộ nhớ cố định tương đối với kích thước đầu vào.
Ví dụ tối giản hoạt động được
// Encoding an API credential pair for an HTTP Basic Auth header
const serviceId = 'deploy-bot'
const apiKey = 'sk-prod-a7f2c91e4b3d8'
const credential = btoa(`${serviceId}:${apiKey}`)
// → 'ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg='
const headers = new Headers({
Authorization: `Basic ${credential}`,
'Content-Type': 'application/json',
})
console.log(headers.get('Authorization'))
// Basic ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=Giải mã với atob()
// Round-trip: encode, transmit, decode const payload = 'service:payments region:eu-west-1 env:production' const encoded = btoa(payload) const decoded = atob(encoded) console.log(encoded) // c2VydmljZTpwYXltZW50cyByZWdpb246ZXUtd2VzdC0xIGVudjpwcm9kdWN0aW9u console.log(decoded === payload) // true
btoa() và atob() là một phần của WinterCG Minimum Common API — spec tương tự điều chỉnh Fetch, URL, và crypto trong các runtime không phải trình duyệt. Chúng hoạt động giống hệt nhau trong Node.js 16+, Bun, Deno, và Cloudflare Workers.Xử lý Unicode và Ký tự Non-ASCII
Bẫy phổ biến nhất của btoa() là giới hạn Latin-1 nghiêm ngặt. Bất kỳ ký tự nào có code point trên U+00FF sẽ gây ra ngoại lệ ngay lập tức:
btoa('Müller & Søren') // ❌ Uncaught DOMException: String contains an invalid character
btoa('résumé') // ❌ 'é' is U+00E9 = 233 — within Latin-1, this one actually works
btoa('田中太郎') // ❌ Throws — all CJK characters are above U+00FFCách tiếp cận đúng là mã hóa chuỗi thành byte UTF-8 trước, sau đó mã hóa Base64 các byte đó. JavaScript cung cấp TextEncoder cho mục đích này:
Cách tiếp cận TextEncoder — an toàn cho mọi đầu vào Unicode
// Utility functions for Unicode-safe Base64
function toBase64(text: string): string {
const bytes = new TextEncoder().encode(text)
const chars = Array.from(bytes, byte => String.fromCharCode(byte))
return btoa(chars.join(''))
}
function fromBase64(encoded: string): string {
const binary = atob(encoded)
const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0))
return new TextDecoder().decode(bytes)
}
// Works with any language or script
const orderNote = 'Xác nhận: Nguyễn Văn Nam — kho Hà Nội, số lượng: 250'
const encoded = toBase64(orderNote)
const decoded = fromBase64(encoded)
console.log(encoded)
// WMOhYyBuaOG6rW46IE5ndXnhu4VuIFbEg24gTmFtIOKAkyBraG8gSMOgIE7hu5lpLCBz4buRIGzGsOG7o25nOiAyNTA=
console.log(decoded === orderNote) // trueBuffer.from(text, 'utf8').toString('base64'). Cách này xử lý Unicode natively và nhanh hơn cho chuỗi lớn.Buffer.from() trong Node.js — Hướng dẫn đầy đủ với Ví dụ
Trong Node.js, Buffer là API thành ngữ cho mọi thao tác dữ liệu nhị phân, bao gồm chuyển đổi encoding. Nó có trước TextEncoder nhiều năm và vẫn là lựa chọn ưu tiên cho mã phía máy chủ. Ưu điểm chính so với btoa(): hỗ trợ UTF-8 native, xử lý dữ liệu nhị phân, và phím tắt encoding 'base64url' có từ Node.js 18.
Mã hóa và giải mã văn bản cơ bản
// Encoding a server configuration object for storage in an env variable
const dbConfig = JSON.stringify({
host: 'db-primary.internal',
port: 5432,
database: 'analytics_prod',
maxConnections: 100,
ssl: { rejectUnauthorized: true },
})
const encoded = Buffer.from(dbConfig, 'utf8').toString('base64')
console.log(encoded)
// eyJob3N0IjoiZGItcHJpbWFyeS5pbnRlcm5hbCIsInBvcnQiOjU0MzIsImRhdGFiYXNlIjoiYW5h...
// Decoding back
const decoded = Buffer.from(encoded, 'base64').toString('utf8')
const config = JSON.parse(decoded)
console.log(config.host) // db-primary.internal
console.log(config.maxConnections) // 100Mã hóa tệp nhị phân từ đĩa
import { readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
// Read a TLS certificate and encode it for embedding in a config file
const certPem = readFileSync(join(process.cwd(), 'ssl', 'server.crt'))
const certBase64 = certPem.toString('base64')
// Store as a single-line string — suitable for env vars or JSON configs
writeFileSync('./dist/cert.b64', certBase64, 'utf8')
console.log(`Certificate encoded: ${certBase64.length} characters`)
// Certificate encoded: 2856 characters
// Restore the binary cert from the encoded value
const restored = Buffer.from(certBase64, 'base64')
console.log(restored.equals(certPem)) // trueMã hóa tệp bất đồng bộ với xử lý lỗi
import { readFile } from 'node:fs/promises'
async function encodeFileToBase64(filePath: string): Promise<string> {
try {
const buffer = await readFile(filePath)
return buffer.toString('base64')
} catch (err) {
const code = (err as NodeJS.ErrnoException).code
if (code === 'ENOENT') throw new Error(`File not found: ${filePath}`)
if (code === 'EACCES') throw new Error(`Permission denied: ${filePath}`)
throw err
}
}
// Encode a PDF for an email attachment payload
const reportBase64 = await encodeFileToBase64('./reports/q1-financials.pdf')
const emailPayload = {
to: 'finance-team@company.internal',
subject: 'Q1 Financial Report',
attachments: [{
filename: 'q1-financials.pdf',
content: reportBase64,
encoding: 'base64',
contentType: 'application/pdf',
}],
}
console.log(`Attachment: ${reportBase64.length} chars`)Hàm Base64 JavaScript — Tham chiếu Tham số
Khác với module base64 của Python, JavaScript không có một hàm Base64 thống nhất duy nhất. API phụ thuộc vào môi trường mục tiêu. Đây là tài liệu tham chiếu đầy đủ cho tất cả các cách tiếp cận native:
| Hàm | Kiểu đầu vào | Unicode | URL-safe | Có sẵn trong |
|---|---|---|---|---|
| btoa(string) | string (Latin-1) | ❌ ném lỗi trên U+00FF | ❌ thay thế thủ công | Browser, Node 16+, Bun, Deno |
| atob(string) | Base64 string | ❌ trả về chuỗi nhị phân | ❌ thay thế thủ công | Browser, Node 16+, Bun, Deno |
| Buffer.from(src, enc) .toString(enc) | string | Buffer | Uint8Array | ✅ encoding utf8 | ✅ base64url trong Node 18+ | Node.js, Bun |
| TextEncoder().encode(str) + btoa() | string (bất kỳ Unicode) | ✅ qua byte UTF-8 | ❌ thay thế thủ công | Browser, Node 16+, Deno |
| Uint8Array.toBase64() (TC39) | Uint8Array | ✅ nhị phân | ✅ omitPadding + alphabet | Chrome 130+, Node 22+ |
Chữ ký Buffer.from(src, enc).toString(enc) chấp nhận một số giá trị encoding liên quan đến Base64:
Base64 URL-safe — Mã hóa cho JWT, URL, và Tên tệp
Base64 chuẩn dùng + và /, vốn là ký tự đặc biệt trong URL — + được giải mã thành dấu cách trong query string, và / là dấu phân cách đường dẫn. JWT, tham số URL, tên tệp, và giá trị cookie đều cần biến thể URL-safe: + → -, / → _, xóa = ở cuối.
Trình duyệt — thay thế ký tự thủ công
function toBase64Url(text: string): string {
// For ASCII-safe input (e.g., JSON with only ASCII chars)
return btoa(text)
.replace(/+/g, '-')
.replace(///g, '_')
.replace(/=/g, '')
}
function fromBase64Url(encoded: string): string {
// Restore standard Base64 characters and padding before decoding
const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/')
const padded = base64 + '==='.slice(0, (4 - base64.length % 4) % 4)
return atob(padded)
}
// JWT header — must be URL-safe Base64
const header = JSON.stringify({ alg: 'HS256', typ: 'JWT' })
const encoded = toBase64Url(header)
console.log(encoded) // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
const decoded = fromBase64Url(encoded)
console.log(decoded) // {"alg":"HS256","typ":"JWT"}Node.js 18+ — encoding 'base64url' native
// Node.js 18 added 'base64url' as a first-class Buffer encoding
const sessionPayload = JSON.stringify({
userId: 'usr_9f2a1c3e8b4d',
role: 'editor',
workspaceId:'ws_3a7f91c2',
exp: Math.floor(Date.now() / 1000) + 3600,
})
const encoded = Buffer.from(sessionPayload, 'utf8').toString('base64url')
// No + or / or = characters in the output
// eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciIsIndvcmtzcGFjZUlkIjoid3NfM2E3ZjkxYzIiLCJleHAiOjE3MTcyMDM2MDB9
const decoded = Buffer.from(encoded, 'base64url').toString('utf8')
console.log(JSON.parse(decoded).role) // editorMã hóa Tệp và Phản hồi API trong JavaScript
Trong mã production, mã hóa Base64 thường được áp dụng nhất cho các tệp đang được truyền và phản hồi từ các API bên ngoài cung cấp nội dung nhị phân. Các mẫu khác nhau giữa trình duyệt và Node.js, và dữ liệu nhị phân cần được xử lý cẩn thận.
Trình duyệt — mã hóa tệp từ phần tử input
// Modern approach: File.arrayBuffer() (Chrome 76+, Firefox 69+, Safari 14+)
async function encodeFile(file: File): Promise<string> {
const buffer = await file.arrayBuffer()
const bytes = new Uint8Array(buffer)
const chars = Array.from(bytes, b => String.fromCharCode(b))
return btoa(chars.join(''))
}
const uploadInput = document.getElementById('avatar') as HTMLInputElement
uploadInput.addEventListener('change', async (e) => {
const file = (e.target as HTMLInputElement).files?.[0]
if (!file) return
try {
const encoded = await encodeFile(file)
const dataUri = `data:${file.type};base64,${encoded}`
// Preview the image inline
const img = document.getElementById('preview') as HTMLImageElement
img.src = dataUri
img.hidden = false
console.log(`Encoded ${file.name} (${file.size} bytes) → ${encoded.length} Base64 chars`)
} catch (err) {
console.error('Encoding failed:', err)
}
})Lấy dữ liệu nhị phân mã hóa Base64 từ API
// GitHub Contents API returns file content as Base64 with embedded newlines
async function fetchRepoFile(
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; size: number }
if (data.encoding !== 'base64') {
throw new Error(`Unexpected encoding from GitHub: ${data.encoding}`)
}
// GitHub wraps output at 60 chars — strip newlines before decoding
const clean = data.content.replace(/\n/g, '')
return atob(clean)
}
const openApiSpec = await fetchRepoFile(
'acme-corp', 'platform-api', 'openapi.json', process.env.GITHUB_TOKEN!
)
const spec = JSON.parse(openApiSpec)
console.log(`API version: ${spec.info.version}`)Khi bạn chỉ cần kiểm tra phản hồi đã mã hóa trong khi debug API mà không cần thiết lập script, hãy dán giá trị Base64 trực tiếp vào Base64 Encoder — nó cũng giải mã, với đầu ra ngay lập tức. Hữu ích để kiểm tra phản hồi GitHub API, payload JWT, và chữ ký webhook.
Mã hóa Base64 từ Command-Line trong Node.js và Shell
Đối với script CI/CD, target Makefile, hoặc debug một lần, bạn hiếm khi cần script đầy đủ. Cả công cụ hệ thống và one-liner Node.js đều bao gồm hầu hết các trường hợp đa nền tảng.
# ── macOS / Linux system base64 ───────────────────────────────────────
# Standard encoding
echo -n "deploy-bot:sk-prod-a7f2c91e4b3d8" | base64
# ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=
# URL-safe variant (replace chars and strip padding)
echo -n "deploy-bot:sk-prod-a7f2c91e4b3d8" | base64 | tr '+/' '-_' | tr -d '='
# Encode a file inline (macOS: -b 0 removes line wrapping; Linux: --wrap=0)
base64 -b 0 ./config/production.json
# or on Linux:
base64 --wrap=0 ./config/production.json
# Decode
echo "ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=" | base64 --decode
# ── Node.js one-liner — works on Windows too ───────────────────────────
node -e "process.stdout.write(Buffer.from(process.argv[1]).toString('base64'))" "my:secret"
# bXk6c2VjcmV0
# URL-safe from Node.js 18+
node -e "process.stdout.write(Buffer.from(process.argv[1]).toString('base64url'))" "my:secret"
# bXk6c2VjcmV0 (same here since there are no special chars)
# Decode in Node.js
node -e "console.log(Buffer.from(process.argv[1], 'base64').toString())" "ZGVwbG95LWJvdA=="base64 mặc định ngắt dòng đầu ra ở 76 ký tự. Điều này phá vỡ quá trình phân tích xuôi dòng. Luôn thêm -b 0 (macOS) hoặc --wrap=0 (Linux) khi bạn cần kết quả một dòng — ví dụ khi ghi vào biến môi trường hoặc trường cấu hình.Giải pháp thay thế Hiệu suất cao: js-base64
Các API tích hợp đã đủ cho hầu hết các trường hợp sử dụng. Lý do chính để dùng thư viện là tính nhất quán xuyên môi trường: nếu bạn ship một package chạy trong cả trình duyệt và Node.js, việc dùng Buffer yêu cầu phát hiện môi trường hoặc cấu hình bundler, trong khi btoa() cần giải pháp Unicode. js-base64 (100M+ lượt tải xuống hàng tuần trên npm) xử lý cả hai một cách trong suốt.
npm install js-base64 # or pnpm add js-base64
import { toBase64, fromBase64, toBase64Url, fromBase64Url, isValid } from 'js-base64'
// Standard encoding — Unicode-safe, works in browser and Node.js
const telemetryEvent = JSON.stringify({
eventId: 'evt_7c3a9f1b2d',
type: 'checkout_completed',
currency: 'EUR',
amount: 14900,
userId: 'usr_4e2b8d6a5c',
timestamp: 1717200000,
})
const encoded = toBase64(telemetryEvent)
const urlEncoded = toBase64Url(telemetryEvent) // No +, /, or = characters
const decoded = fromBase64(encoded)
console.log(JSON.parse(decoded).type) // checkout_completed
// Binary data — pass a Uint8Array as second argument
const pngMagicBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])
const binaryEncoded = toBase64(pngMagicBytes, true) // true = binary mode
// Validation before decoding
const suspicious = 'not!valid@base64#'
console.log(isValid(suspicious)) // falseBên dưới, js-base64 dùng Buffer native khi có sẵn và fallback về implementation pure-JS trong trình duyệt. Nó nhanh hơn 2–3× so với cách tiếp cận TextEncoder+btoa cho chuỗi Unicode lớn, và API đối xứng ( toBase64 / fromBase64) loại bỏ gánh nặng tinh thần khi nhớ chiều của btoa và atob.
Mã hóa Tệp Nhị phân Lớn với Node.js Streams
Khi bạn cần mã hóa các tệp lớn hơn ~50 MB, việc tải toàn bộ tệp vào bộ nhớ bằng readFileSync() trở thành vấn đề. Stream Node.js cho phép bạn xử lý dữ liệu theo từng chunk — nhưng mã hóa Base64 có một ràng buộc: bạn phải cung cấp dữ liệu cho encoder theo bội số 3 byte để tránh padding sai ở ranh giới chunk.
import { createReadStream, createWriteStream } from 'node:fs'
import { pipeline } from 'node:stream/promises'
// Stream a large binary file to a Base64-encoded output file
async function streamEncodeToBase64(
inputPath: string,
outputPath: string,
): Promise<void> {
const readStream = createReadStream(inputPath, { highWaterMark: 3 * 1024 * 256 }) // 768 KB chunks (multiple of 3)
const writeStream = createWriteStream(outputPath, { encoding: 'utf8' })
let buffer = Buffer.alloc(0)
await pipeline(
readStream,
async function* (source) {
for await (const chunk of source) {
buffer = Buffer.concat([buffer, chunk as Buffer])
// Encode in complete 3-byte groups to avoid mid-stream padding
const remainder = buffer.length % 3
const safe = buffer.subarray(0, buffer.length - remainder)
buffer = buffer.subarray(buffer.length - remainder)
if (safe.length > 0) yield safe.toString('base64')
}
// Flush remaining bytes (may add 1 or 2 '=' padding chars)
if (buffer.length > 0) yield buffer.toString('base64')
},
writeStream,
)
}
// Usage: encode a 200 MB video attachment
await streamEncodeToBase64(
'./uploads/product-demo.mp4',
'./dist/product-demo.b64',
)
console.log('Stream encoding complete')= giả ở giữa đầu ra. Ví dụ sử dụng 3 * 1024 * 256 = 786.432 byte (768 KB) — điều chỉnh highWaterMark dựa trên ngân sách bộ nhớ của bạn. Với các tệp dưới 50 MB, readFile() + Buffer.toString('base64') đơn giản hơn và đủ nhanh.Các Lỗi Thường Gặp
Tôi đã xem xét nhiều codebase JavaScript với mã hóa Base64, và bốn lỗi này xuất hiện liên tục — thường không được phát hiện cho đến khi ký tự non-ASCII hoặc tệp nhị phân đến đường dẫn mã hóa trong môi trường production.
Lỗi 1 — Truyền Unicode trực tiếp vào btoa()
Vấn đề: btoa() chỉ chấp nhận ký tự có code point 0–255. Các ký tự như ñ, emoji, hoặc ký tự tượng hình CJK gây ra DOMException ngay lập tức. Giải pháp: mã hóa với TextEncoder trước, hoặc dùng Buffer.from(text, 'utf8').toString('base64') trong Node.js.
// ❌ DOMException: The string to be encoded contains // characters outside of the Latin1 range const username = 'Trần Thị Mai' const encoded = btoa(username) // throws
// ✅ Encode as UTF-8 bytes first
function safeEncode(text: string): string {
const bytes = new TextEncoder().encode(text)
const chars = Array.from(bytes, b => String.fromCharCode(b))
return btoa(chars.join(''))
}
const encoded = safeEncode('Trần Thị Mai')
// VHLhuqduIFRo4buLIE1haQ==Lỗi 2 — Quên khôi phục padding trước atob()
Vấn đề: Base64 URL-safe xóa padding =. Truyền chuỗi đã xóa padding trực tiếp vào atob() tạo ra đầu ra sai hoặc ném lỗi tùy thuộc vào độ dài chuỗi. Giải pháp: khôi phục + và / và thêm lại lượng padding chính xác trước khi gọi atob().
// ❌ atob() may return wrong data or throw // on URL-safe Base64 without padding const jwtSegment = 'eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ' const decoded = atob(jwtSegment) // Unreliable
// ✅ Restore characters 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)
return atob(pad)
}
const decoded = decodeBase64Url('eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ')
// {"userId":"usr_9f2a1c3e"}Lỗi 3 — Ghép nối các chunk đã mã hóa thay vì buffer thô
Vấn đề: Mỗi lần gọi btoa() hoặc .toString('base64') thêm padding riêng. Ghép nối hai chuỗi Base64 đã có padding tạo ra đầu ra không hợp lệ vì padding chỉ nên ở cuối cùng. Giải pháp: ghép nối dữ liệu thô trước khi mã hóa.
// ❌ Both parts are padded independently —
// the combined string is not valid Base64
const part1 = Buffer.from('webhook-secret').toString('base64')
// d2ViaG9vay1zZWNyZXQ= ← has padding
const part2 = Buffer.from('-v2').toString('base64')
// LXYy ← correct in isolation
const combined = part1 + part2 // ❌ Invalid — padding in the middle// ✅ Concatenate raw Buffers before encoding
const combined = Buffer.concat([
Buffer.from('webhook-secret'),
Buffer.from('-v2'),
]).toString('base64')
// d2ViaG9vay1zZWNyZXQtdjI= — single valid Base64 stringLỗi 4 — Dùng response.text() để đọc dữ liệu API nhị phân trước khi mã hóa
Vấn đề: response.text() giải thích các byte thô là UTF-8 và thay thế các chuỗi byte không được nhận dạng bằng ký tự thay thế U+FFFD. Bất kỳ nội dung nhị phân nào — hình ảnh, PDF, âm thanh — bị hỏng âm thầm trước khi đến btoa(). Giải pháp: dùng response.arrayBuffer() để lấy byte thô.
// ❌ response.text() corrupts binary data
const res = await fetch('/api/exports/invoice.pdf')
const text = await res.text() // ❌ PDF bytes mangled as UTF-8
const encoded = btoa(text) // ❌ Corrupted Base64// ✅ arrayBuffer() preserves raw bytes
const res = await fetch('/api/exports/invoice.pdf')
const buffer = await res.arrayBuffer()
const bytes = new Uint8Array(buffer)
const chars = Array.from(bytes, b => String.fromCharCode(b))
const encoded = btoa(chars.join('')) // ✅ Valid Base64Các Phương thức Base64 JavaScript — So sánh Nhanh
| Phương thức | Unicode | Dữ liệu nhị phân | URL-safe | Môi trường | Cần cài đặt |
|---|---|---|---|---|---|
| btoa() / atob() | ❌ Latin-1 | ❌ cần giải pháp thay thế | ❌ thay thế thủ công | Browser, Node 16+, Bun, Deno | Không |
| TextEncoder + btoa() | ✅ UTF-8 | ✅ qua Uint8Array | ❌ thay thế thủ công | Browser, Node 16+, Deno | Không |
| Buffer.from().toString() | ✅ utf8 | ✅ native | ✅ base64url (Node 18+) | Node.js, Bun | Không |
| Uint8Array.toBase64() (TC39) | ✅ nhị phân | ✅ native | ✅ tùy chọn alphabet | Chrome 130+, Node 22+ | Không |
| js-base64 | ✅ luôn | ✅ Uint8Array | ✅ tích hợp sẵn | Đa năng | npm install |
Chọn btoa() chỉ khi đầu vào được xác minh là chỉ ASCII — hex digest, ID số, hoặc chuỗi Latin-1 đã được xác thực trước. Với văn bản người dùng cung cấp trong trình duyệt, dùng TextEncoder + btoa(). Với toàn bộ mã Node.js phía máy chủ, Buffer là mặc định đúng. Với thư viện cần chạy trong cả hai môi trường mà không cần cấu hình bundler, js-base64 loại bỏ tất cả các trường hợp biên.
Câu hỏi Thường gặp
Công cụ Liên quan
Để mã hóa hoặc giải mã một cú click mà không cần viết code, dán chuỗi hoặc dữ liệu nhị phân của bạn trực tiếp vào Base64 Encoder — xử lý cả chế độ chuẩn và URL-safe ngay lập tức trong trình duyệt của bạn.
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.