JavaScript Base64 编码指南:btoa() 与 Buffer 详解

·Front-end & Node.js Developer·审阅者Sophie Laurent·发布日期

直接在浏览器中使用免费的 Base64编码器,无需安装。

在线试用 Base64编码器 →

当你在 CSS data URI 中嵌入图片、在 HTTP Authorization 头中传递凭证,或将二进制证书存储到环境变量时, 你需要在浏览器和 Node.js 中可靠地对 JavaScript 数据进行 Base64 编码。JavaScript 提供了两种不同的内置 API:btoa() 用于浏览器环境(Node.js 16+ 中也可用),Buffer.from() 用于 Node.js — 两者在 Unicode、二进制数据和 URL 安全性方面各有限制。如需无需编写代码的快速编码, ToolDeck's Base64 Encoder 可在浏览器中即时完成。本指南涵盖两种环境的生产级示例:Unicode 处理、URL 安全变体、文件与 API 响应编码、CLI 用法, 以及在真实代码库中反复出现的四个常见错误。

  • btoa() 是浏览器原生函数,在 Node.js 16+ 中也可全局使用,但仅接受 Latin-1(码位 0–255)— 传入 Unicode 字符会抛出 DOMException
  • Buffer.from(text, "utf8").toString("base64") 是 Node.js 的对应方法,原生支持 Unicode,无需额外处理
  • URL 安全 Base64 将 + 替换为 -、/ 替换为 _,并去除 = 填充 — 在 Node.js 18+ 中使用 Buffer.from().toString("base64url") 可一步完成
  • 对于二进制数据(ArrayBuffer、Uint8Array、文件),在 Node.js 中使用 Buffer,在浏览器中使用 arrayBuffer() + Uint8Array 方法 — 切勿使用 response.text()
  • Uint8Array.prototype.toBase64()(TC39 Stage 3)已在 Node.js 22+ 和 Chrome 130+ 中可用,将统一两种环境的处理方式

什么是 Base64 编码?

Base64 将任意二进制数据转换为由 64 个可打印 ASCII 字符组成的字符串: A–Z、a–z、0–9、+ /。每 3 个字节的输入恰好映射为 4 个 Base64 字符; 若输入长度不是 3 的倍数,则会追加一或两个 = 填充字符。编码后的输出比原始数据大约多 33%。

Base64 不是加密 — 它不提供任何保密性。任何人拿到编码字符串都可以通过一次函数调用解码。 其目的是传输安全:许多协议和存储格式是为 7 位 ASCII 文本设计的,无法处理任意二进制字节。 Base64 弥补了这一差距。JavaScript 常见使用场景包括:内联资源的 data URI、HTTP Basic Auth 头、 JWT 令牌片段、电子邮件 MIME 附件,以及在 JSON API 中存储二进制数据。

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

btoa() — 浏览器原生编码函数

btoa()(binary-to-ASCII)自 IE10 起就已在浏览器中可用, 并作为 WinterCG 兼容性计划的一部分,在 Node.js 16.0 中成为全局函数。它在 Deno、Bun 和 Cloudflare Workers 中也原生可用,无需任何导入。

该函数接受一个字符串参数并返回其 Base64 编码形式。对称的反向函数 atob()(ASCII-to-binary)用于解码。 两者均为同步操作,内存消耗与输入大小成线性关系。

最小可运行示例

JavaScript (browser / Node.js 16+)
// 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=

使用 atob() 解码

JavaScript
// 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()atob() WinterCG Minimum Common API 的一部分 — 该规范同样管理非浏览器运行时中的 Fetch、URL 和 crypto。 它们在 Node.js 16+、Bun、Deno 和 Cloudflare Workers 中行为完全一致。

处理 Unicode 和非 ASCII 字符

btoa() 最常见的陷阱是其严格的 Latin-1 边界。 任何码位超过 U+00FF 的字符都会立即引发异常:

JavaScript
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+00FF

正确的做法是先将字符串编码为 UTF-8 字节,再对这些字节进行 Base64 编码。 JavaScript 提供了专门用于此目的的 TextEncoder

TextEncoder 方案 — 适用于任意 Unicode 输入

JavaScript (browser + Node.js 16+)
// 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 = '已确认:张伟 — 上海仓库,数量:250'
const encoded   = toBase64(orderNote)
const decoded   = fromBase64(encoded)

console.log(encoded)
// 5bey56S+6KKr5byg5LqG77ya5byg5oiV5LuK5Y+k5Zub55qE5Lo25pe277yM5pWZ6K+V5a6577yaMjUw

console.log(decoded === orderNote) // true
注意:如果你已经在使用 Node.js,可以完全跳过 TextEncoder 这个变通方法 — 使用 Buffer.from(text, 'utf8').toString('base64')。 它原生支持 Unicode,处理大字符串时速度更快。

Node.js 中的 Buffer.from() — 完整指南与示例

在 Node.js 中,Buffer 是处理所有二进制数据操作(包括编码转换)的惯用 API。 它比 TextEncoder 早了数年,至今仍是服务端代码的首选。 相较于 btoa() 的主要优势:原生 UTF-8 支持、二进制数据处理, 以及 Node.js 18 起提供的 'base64url' 编码快捷方式。

基本文本编码与解码

Node.js
// 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) // 100

从磁盘编码二进制文件

Node.js
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)) // true

带错误处理的异步文件编码

Node.js
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`)

JavaScript Base64 函数 — 参数参考

与 Python 的 base64 模块不同,JavaScript 没有单一统一的 Base64 函数。 使用的 API 取决于目标环境。以下是所有原生方案的完整参考:

函数输入类型UnicodeURL 安全可用环境
btoa(string)string (Latin-1)❌ 超出 U+00FF 即抛出异常❌ 手动替换Browser, Node 16+, Bun, Deno
atob(string)Base64 string❌ 返回二进制字符串❌ 手动替换Browser, Node 16+, Bun, Deno
Buffer.from(src, enc) .toString(enc)string | Buffer | Uint8Array✅ utf8 编码✅ Node 18+ 中的 base64urlNode.js, Bun
TextEncoder().encode(str) + btoa()string(任意 Unicode)✅ 通过 UTF-8 字节❌ 手动替换Browser, Node 16+, Deno
Uint8Array.toBase64() (TC39)Uint8Array✅ 二进制✅ omitPadding + alphabetChrome 130+, Node 22+

Buffer.from(src, enc).toString(enc) 签名接受与 Base64 相关的多个编码值:

"base64"
标准 Base64(RFC 4648 §4)。使用 + 和 /,并带 = 填充。
"base64url"
URL 安全 Base64(RFC 4648 §5,Node.js 18+)。使用 - 和 _,不带填充。
"utf8"
字符串来源的默认值。当来源为人类可读文本时使用。
"binary"
Latin-1 / ISO-8859-1。当来源为原始二进制字符串(如来自 atob())时使用。

URL 安全 Base64 — 用于 JWT、URL 和文件名的编码

标准 Base64 使用 + /,而这两个字符在 URL 中是保留字符 — + 在查询字符串中会被解码为空格, / 是路径分隔符。JWT、URL 参数、文件名和 Cookie 值都需要 URL 安全变体:+- /_,去除末尾的 =

浏览器 — 手动字符替换

JavaScript (browser)
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+ — 原生 'base64url' 编码

Node.js 18+
// 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) // editor

在 JavaScript 中编码文件和 API 响应

在生产代码中,Base64 编码最常用于传输中的文件以及来自外部 API 的二进制内容响应。 浏览器和 Node.js 的处理模式不同,二进制数据需要特别小心。

浏览器 — 从 input 元素编码文件

JavaScript (browser)
// 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)
  }
})

从 API 获取 Base64 编码的二进制数据

JavaScript
// 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}`)

调试 API 时如需检查编码后的响应,无需编写脚本,直接将 Base64 值粘贴到 Base64 Encoder 中即可 — 它同样支持解码,输出即时呈现。适合检查 GitHub API 响应、JWT 负载和 webhook 签名。

在 Node.js 和 Shell 中进行命令行 Base64 编码

对于 CI/CD 脚本、Makefile 目标或一次性调试,通常不需要完整脚本。 系统工具和 Node.js 单行命令可跨平台覆盖大多数场景。

bash
# ── 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=="
注意:在 macOS 上,base64 默认会在 76 个字符处换行。 这会导致下游解析失败。需要单行结果时(例如写入环境变量或配置字段),务必添加 -b 0(macOS)或 --wrap=0(Linux)。

高性能替代方案:js-base64

内置 API 对大多数场景已足够。选择第三方库的主要原因是跨环境一致性: 如果你发布的包需要同时在浏览器和 Node.js 中运行,使用 Buffer 需要环境检测或打包器配置, 而 btoa() 则需要 Unicode 变通方案。js-base64(npm 周下载量超 1 亿)可透明地处理两种情况。

bash
npm install js-base64
# or
pnpm add js-base64
JavaScript
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)) // false

在底层,js-base64 在可用时使用原生 Buffer,在浏览器中回退到纯 JS 实现。 对于大型 Unicode 字符串,其速度比 TextEncoder+btoa 方案快 2–3 倍, 且对称的 API(toBase64 / fromBase64)消除了记忆 btoa atob 方向的心智负担。

使用 Node.js 流编码大型二进制文件

当需要编码大于约 50 MB 的文件时,使用 readFileSync() 将整个文件加载到内存会造成问题。 Node.js 流允许分块处理数据 — 但 Base64 编码有一个限制:必须以 3 字节的倍数喂给编码器, 以避免在块边界处出现错误的填充。

Node.js
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')
注意:块大小必须是 3 字节的倍数,以避免在输出中间出现多余的 = 填充。示例使用 3 * 1024 * 256 = 786,432 字节(768 KB)— 根据内存预算调整 highWaterMark。 对于 50 MB 以下的文件,readFile() + Buffer.toString('base64') 更简单且足够快。

常见错误

在审查众多包含 Base64 编码的 JavaScript 代码库后,这四个错误反复出现 — 通常在非 ASCII 字符或二进制文件进入生产环境的编码路径时才被发现。

错误 1 — 将 Unicode 直接传递给 btoa()

问题: btoa() 只接受码位 0–255 的字符。ñ、emoji 或 CJK 汉字等字符会立即导致 DOMException 解决方案: 先使用 TextEncoder 编码,或在 Node.js 中使用 Buffer.from(text, 'utf8').toString('base64')

Before · JavaScript
After · JavaScript
// ❌ DOMException: The string to be encoded contains
//    characters outside of the Latin1 range
const username = 'Алексей Иванов'
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('Алексей Иванов')
// 0JDQu9C10LrRgdC10Lkg0JjQstCw0L3QvtCy

错误 2 — 调用 atob() 前忘记恢复填充

问题: URL 安全 Base64 会去除 = 填充。 将去除填充的字符串直接传给 atob(), 根据字符串长度不同,可能产生错误输出或抛出异常。 解决方案: 在调用 atob() 前,先恢复 +/ 并补充正确数量的填充。

Before · JavaScript
After · JavaScript
// ❌ 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"}

错误 3 — 拼接已编码的块而非原始缓冲区

问题: 每次调用 btoa() .toString('base64') 都会添加自己的填充。 拼接两个带填充的 Base64 字符串会产生无效输出,因为填充只应出现在末尾。 解决方案: 先拼接原始数据,再进行编码。

Before · JavaScript
After · JavaScript
// ❌ 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 string

错误 4 — 编码前使用 response.text() 读取二进制 API 数据

问题: response.text() 将原始字节解释为 UTF-8, 并用替换字符 U+FFFD 替换无法识别的字节序列。 任何二进制内容(图片、PDF、音频)在到达 btoa() 之前都会被悄然损坏。 解决方案: 使用 response.arrayBuffer() 获取原始字节。

Before · JavaScript
After · JavaScript
// ❌ 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 Base64

JavaScript Base64 方法 — 快速对比

方法Unicode二进制数据URL 安全可用环境需要安装
btoa() / atob()❌ Latin-1❌ 需要变通❌ 手动替换Browser, Node 16+, Bun, Deno
TextEncoder + btoa()✅ UTF-8✅ 通过 Uint8Array❌ 手动替换Browser, Node 16+, Deno
Buffer.from().toString()✅ utf8✅ 原生✅ base64url (Node 18+)Node.js, Bun
Uint8Array.toBase64() (TC39)✅ 二进制✅ 原生✅ alphabet 选项Chrome 130+, Node 22+
js-base64✅ 始终✅ Uint8Array✅ 内置通用npm install

仅在输入可证明为纯 ASCII 时(十六进制摘要、数字 ID 或经过验证的 Latin-1 字符串)使用 btoa()。对于浏览器中的用户输入文本,使用 TextEncoder + btoa()。 对于所有 Node.js 服务端代码,Buffer 是正确的默认选择。 对于需要在两种环境中无需打包器配置即可运行的库, js-base64 可消除所有边界情况。

常见问题

为什么 btoa() 对我的字符串抛出 "InvalidCharacterError"?
btoa() 只接受码位在 0–255 范围内(Latin-1 / ISO-8859-1)的字符。任何超过 U+00FF 的字符——包括大多数西里尔文、阿拉伯文、CJK 汉字和许多 emoji——都会导致 DOMException。修复方法取决于你的环境:在浏览器中,先用 TextEncoder 将文本编码为 UTF-8 字节,用 String.fromCharCode() 将每个字节转换为字符,再调用 btoa()。在 Node.js 中,使用 Buffer.from(text, 'utf8').toString('base64'),它原生支持 Unicode。
btoa() 在 Node.js 中无需任何导入就可以使用吗?
是的,自 Node.js 16.0 起。btoa() 和 atob() 均注册为全局函数——无需导入。它们与浏览器对应函数行为完全一致,包括 Latin-1 限制。对于 Node.js 服务端代码,仍然推荐使用 Buffer.from() 而非 btoa(),因为它原生处理 UTF-8,无需变通方案即可支持二进制数据,并且在 Node.js 18 中新增了 'base64url' 编码选项。
标准 Base64 和 URL 安全 Base64 有什么区别?
标准 Base64(RFC 4648 §4)使用 + 表示值 62,/ 表示值 63,= 用于填充。这些字符在 URL 中有特殊含义:+ 在查询字符串中被解释为空格,/ 是路径分隔符。URL 安全 Base64(RFC 4648 §5)用 - 替代 +,用 _ 替代 /,通常完全省略 = 填充。JWT 对所有三个片段都使用 URL 安全 Base64。在 Node.js 18+ 中,Buffer.from(text).toString('base64url') 可直接生成 URL 安全格式。
如何在 JavaScript 中将图片编码为 Base64 用于 CSS data URI?
在浏览器中:使用 file.arrayBuffer() 读取二进制数据,转换为 Uint8Array,然后调用 btoa(Array.from(bytes, b => String.fromCharCode(b)).join(''))。将 data URI 构建为 'data:' + file.type + ';base64,' + encoded。在 Node.js 中:const encoded = fs.readFileSync('./image.png').toString('base64'),并在前面加上 MIME 类型。对于 SVG 文件,通常可以完全跳过 Base64,改用 URL 编码的 data URI,可读性更好且略小。
我能在浏览器中不使用任何 npm 库进行 Base64 编码和解码吗?
可以。对于纯 ASCII 输入,btoa() 和 atob() 可直接使用。对于 Unicode,TextEncoder / TextDecoder 组合提供了完整工具集——两者都内置于所有现代浏览器和 Node.js 16+ 中。只有在需要跨环境一致性时,库才真正有价值:如果你编写的工具需要在浏览器和 Node.js 中无需打包器配置即可完全相同地工作,js-base64 可消除环境检测逻辑。
如何解码 GitHub API 返回的 Base64 内容?
GitHub Contents API 返回的文件内容是带有嵌入换行符的 Base64(API 在 60 个字符处换行)。解码前先去除换行符:const clean = data.content.replace(/\n/g, ''); const text = atob(clean);。在 Node.js 中:const text = Buffer.from(data.content.replace(/\n/g, ''), 'base64').toString('utf8');。GitHub 始终使用标准 Base64(非 URL 安全),因此无需进行 + → - 或 / → _ 的替换。

相关工具

无需编写任何代码,一键编码或解码,将你的字符串或二进制数据直接粘贴到 Base64 Encoder 中 — 它在浏览器中即时处理标准和 URL 安全模式。

也支持:PythonJava
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 Laurent技术审阅者

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.