Загрузить файл
  1. base64hub
  2. Статьи о Base64
  3. Uint8Array.toBase64()
Руководство

Uint8Array.toBase64 и fromBase64 в JavaScript

В JavaScript наконец появился Base64 API, который работает с байтами, понимает Base64URL и позволяет выбрать строгость декодирования. Он заменяет большинство случаев использования btoa, atob и самописных циклов.

Кодирование и декодирование одной строкой

API добавляет три метода для Base64 и три для hex:

МетодЧто делает
bytes.toBase64(options)Uint8Array в строку Base64
Uint8Array.fromBase64(string, options)Строку Base64 в новый Uint8Array
bytes.setFromBase64(string, options)Декодирует в существующий Uint8Array
bytes.toHex()Uint8Array в hex-строку в нижнем регистре
Uint8Array.fromHex(string)Hex-строку в новый Uint8Array
bytes.setFromHex(string)Декодирует hex в существующий Uint8Array

Текст сначала нужно превратить в байты. TextEncoder всегда выдаёт UTF-8, поэтому Unicode работает без всяких фокусов:

const bytes = new TextEncoder().encode("Grüße, 世界");
const b64 = bytes.toBase64();
console.log(b64); // "R3LDvMOfZSwg5LiW55WM"

const back = new TextDecoder().decode(Uint8Array.fromBase64(b64));
console.log(back); // "Grüße, 世界"

Для ArrayBuffer, например из fetch().arrayBuffer() или crypto.subtle.digest(), нужно представление: new Uint8Array(buffer).toBase64(). У других типизированных массивов этих методов нет вовсе. Чтобы закодировать сырую память Uint16Array, оберните её: new Uint8Array(u16.buffer, u16.byteOffset, u16.byteLength).

Опции: alphabet, omitPadding, lastChunkHandling

alphabet

"base64" (по умолчанию) использует + и /. "base64url" использует - и _. Опция действует и на кодирование, и на декодирование, а декодер, настроенный на один алфавит, отвергает символы другого.

const data = new Uint8Array([251, 255]);
data.toBase64();                                         // "+/8="
data.toBase64({ alphabet: "base64url" });                // "-_8="
data.toBase64({ alphabet: "base64url", omitPadding: true }); // "-_8"

Uint8Array.fromBase64("-_8", { alphabet: "base64url" }); // Uint8Array [251, 255]
Uint8Array.fromBase64("-_8");                            // SyntaxError

omitPadding

toBase64 по умолчанию добавляет дополнение =, даже с алфавитом Base64URL. Для вывода в стиле JWT передайте omitPadding: true. При декодировании дополнением управляет lastChunkHandling.

lastChunkHandling

Эта опция решает, что fromBase64 и setFromBase64 делают с последней группой символов. Три режима, проверенные на вводе, который декодируется в байты Hi?>:

Ввод"loose" (по умолчанию)"strict""stop-before-partial"
SGk/Pg==Hi?>Hi?>Hi?>
SGk/Pg (без дополнения)Hi?>SyntaxErrorHi? (останавливается перед Pg)
SGk/Ph== (ненулевые биты-заполнители)Hi?>SyntaxErrorHi?>
SGk/P (один оставшийся символ)SyntaxErrorSyntaxErrorHi?
  • loose ведёт себя как atob: дополнение необязательно, лишние биты в последнем символе игнорируются.
  • strict принимает только канонический ввод с дополнением, как его описывает RFC 4648. Используйте его, когда закодированная форма должна быть единственной, например если строки Base64 сравниваются или подписываются.
  • stop-before-partial декодирует все полные группы по 4 символа и не трогает неполный хвост. Это нужно для потоковой обработки, когда остаток группы придёт в следующем куске.

В любом режиме пробельные символы ASCII (пробелы, табуляции, переводы строк) пропускаются, так что ввод с переносами в стиле MIME декодируется напрямую. Любой другой символ вне алфавита вызывает SyntaxError.

Запись в существующий буфер

setFromBase64 и setFromHex декодируют в уже имеющийся Uint8Array и сообщают, сколько использовали:

const target = new Uint8Array(8);
const result = target.setFromBase64("SGVsbG8gd29ybGQ=");
console.log(result); // { read: 8, written: 6 }

Hello world занимает 11 байт, а в буфер помещается 8. Декодер записывает только целые 3-байтные группы, которые влезают, поэтому останавливается после 6 байт и сообщает, что прочитал 8 символов. Чтобы продолжить, вызовите метод снова с остатком строки и subarray буфера. Вместе с stop-before-partial это позволяет декодировать поток кусок за куском в заранее выделенный буфер:

const out = new Uint8Array(1024);
const { read, written } = out.setFromBase64("SGk/Pg", {
  lastChunkHandling: "stop-before-partial",
});
// read: 4, written: 3. Keep "Pg" and prepend it to the next chunk.

Методы для hex устроены так же. toHex() всегда пишет в нижнем регистре, fromHex() принимает оба регистра, а на нечётном количестве цифр выбрасывает SyntaxError:

new Uint8Array([0xde, 0xad, 0xbe, 0xef]).toHex(); // "deadbeef"
Uint8Array.fromHex("CAFE");                       // Uint8Array [202, 254]

Чем неудобны btoa и atob

btoa и atob родом из ранних браузеров и работают с «двоичными строками»: строками JavaScript, где код каждого символа равен значению байта от 0 до 255. Отсюда три проблемы.

Unicode либо вызывает исключение, либо молча ломается. Символы выше U+00FF заставляют btoa выбросить InvalidCharacterError (в Chrome: «The string to be encoded contains characters outside of the Latin1 range»). Символы от U+0080 до U+00FF ещё хуже: они кодируются без ошибки, но как Latin-1. btoa("Grüße") возвращает R3L832U=, хотя все остальные системы ожидают кодирование UTF-8, то есть R3LDvMOfZQ==.

Декодирование возвращает строку вместо байтов. atob("Q2Fmw6k=") возвращает "Café". Чтобы получить настоящий текст, нужно перевести строку в байты и декодировать их как UTF-8, а это цикл или Uint8Array.from(atob(s), c => c.charCodeAt(0)).

С большими данными нужна осторожность. Популярный однострочник btoa(String.fromCharCode(...bytes)) передаёт каждый байт как аргумент функции, и на массиве в 1 МБ падает с RangeError: Maximum call stack size exceeded. Строку приходится собирать кусками.

Кроме того, нет поддержки Base64URL и никакого контроля над дополнением и строгостью. Старый обходной путь btoa(unescape(encodeURIComponent(text))) действительно даёт Base64 от UTF-8, но опирается на устаревший unescape. С новым API всё это сводится к new TextEncoder().encode(text).toBase64().

Поддержка в браузерах и Node.js

Методы пришли из предложения TC39 «Uint8Array to/from Base64», которое достигло Stage 4 в июле 2025 года и вошло в ECMAScript 2026.

СредаПервая версия со всеми шестью методами
Chrome, Edge140
Firefox133
Safari (macOS и iOS)18.2
Node.js25.0.0

С выходом Chrome 140 в сентябре 2025 года возможность получила статус Baseline, так что она есть во всех актуальных основных браузерах. В Node.js мы проверили методы напрямую. В Node 25.0.0 и 26.12.1 есть все шесть. В Node 24.22.1, LTS-релизе, по умолчанию нет ни одного. Его V8 принимает экспериментальный флаг --js-base-64, но в нашем тесте этот флаг открыл toBase64, fromBase64, toHex и fromHex без setFromBase64 и setFromHex, так что считайте Node 24 неподдерживаемым. В Node 24 и более ранних версиях используйте Buffer.from(b64, "base64") и buf.toString("base64"), которые принимают и "base64url". По данным совместимости MDN, API поддерживают также Deno 2.5 и Bun 1.1.22.

Запасной вариант для старых сред

Проверяйте наличие метода и откатывайтесь на btoa/atob с обработкой кусками. Эта версия проверена на Node 24 (запасной путь) и Node 26 (нативный путь) с текстом в UTF-8 и массивом в 1 МБ:

function bytesToBase64(bytes) {
  if (typeof bytes.toBase64 === "function") return bytes.toBase64();
  let bin = "";
  for (let i = 0; i < bytes.length; i += 0x8000) {
    bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
  }
  return btoa(bin);
}

function base64ToBytes(b64) {
  if (typeof Uint8Array.fromBase64 === "function") return Uint8Array.fromBase64(b64);
  return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
}

Запасной вариант покрывает только опции по умолчанию. Если base64url, strict или setFromBase64 нужны везде, используйте полноценный полифил: в core-js он есть в виде модулей es.uint8-array.to-base64, es.uint8-array.from-base64 и связанных с ними.

Попробовать кодирование без кода можно в кодировщике Base64 и декодере, а URL-safe алфавит в Base64URL. Больше примеров на JavaScript, включая Buffer в Node, собрано в статьях Base64 в JavaScript и Base64 в Node.js.

Вопросы и ответы

Можно ли уже использовать новый Base64 API в продакшене?

В браузерах да: он есть в Chrome и Edge 140, Firefox 133 и Safari 18.2. В Node.js нужна версия 25 или новее. Если вы поддерживаете старые браузеры или Node 24 и ниже, оставьте запасной вариант.

Принимает ли Uint8Array.fromBase64 Base64URL?

Да, с опцией { alphabet: "base64url" }. Без неё - и _ вызывают SyntaxError. В режиме loose по умолчанию дополнение необязательно для обоих алфавитов.

Как закодировать строку через toBase64?

Сначала превратите её в байты UTF-8: new TextEncoder().encode(text).toBase64(). Декодирование делается обратной операцией: new TextDecoder().decode(Uint8Array.fromBase64(b64)).

Почему у моего Buffer или Uint16Array нет метода toBase64?

Методы есть только у Uint8Array и его подклассов. Buffer в Node получает их начиная с Node 25. Для других типизированных массивов оберните память: new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength).

Читайте также