Инструменты разработчика · Кодер и декодер Base64
Почему btoa() выдает эмодзи и как закодировать UTF-8 в Base64 в JavaScript
· Как это работает
base64 Юникод кодирование
btoa() принимает символы только до U+00FF, поэтому текст с акцентом, CJK и эмодзи. В этом посте показано, что на самом деле ожидает функция и как TextEncoder получает правильную строку UTF-8 Base64.
Почему btoa может использовать Unicode и незаметно неправильно закодировать акцент
Вызов btoa("😀") выдает ошибку InvalidCharacterError, поскольку эмодзи не может уместиться в одну кодовую единицу размером в один байт. Более тонкая ошибка — btoa("é"): предварительно составленный é — это U+00E9, ниже 256, поэтому btoa принимает его, но кодирует Latin-1 byte E9, а не UTF-8 байт C3 A9. Тот же видимый акцент, записанный как e, плюс комбинированная отметка могут быть выброшены, поскольку отметка находится за пределами принятого диапазона. Сокращение рабочей книги «выбрасывает акцент» нуждается в этом уточнении: строка может громко терпеть неудачу или незаметно выдать неправильные байты.
Что на самом деле кодирует btoa(): двоичная строка кодовых единиц 0–255 — почему функция была разработана на основе Latin-1 bytes, а не текста Unicode
btoa использует «двоичную строку»: каждая единица кода символа JavaScript должна находиться в диапазоне 0–255 и соответствовать одному байту. Он не понимает кодировку текста, язык или нормализацию Unicode. Астральный эмодзи представлен двумя суррогатными кодовыми единицами UTF-16, обе намного больше, чем 255, поэтому отправка необработанной строки JavaScript напрямую не может работать. Рассматривайте выходные данные как кодировку байтов, а не абстрактных символов.
Сначала UTF-8, затем Base64 — почему текст должен стать байтом, прежде чем будет применен какой-либо алфавит Base64
TextEncoder сначала преобразует строку JavaScript в последовательность байтов UTF-8. Затем преобразуйте каждый байт в символ двоичной строки и передайте эту двоичную строку в btoa или используйте другой API, который принимает байты напрямую. Для декодирования atob возвращает двоичную строку; восстановить его байтовые значения и передать их TextDecoder("utf-8"). ToolAcre использует строгий декодер, который отклоняет неверный формат UTF-8 вместо автоматической вставки символов замены.
Рабочий пример: кодирование «café 😀» с помощью TextEncoder и btoa — последовательность байтов, промежуточная двоичная строка и конечный результат.
Для обычного текстового кафе 😀 байты UTF-8 имеют вид 63 61 66 C3 A9 20 F0 9F 98 80 в шестнадцатеричном формате: ASCII c-a-f, два байта для é, пробел и четыре байта для эмодзи. Base64 из этих десяти байтов — Y2Fmw6kg8J+YgA==. Заполнение и алфавит описывают только байты; они не маркируют язык. Сравните прямой сбой btoa("café 😀") с режимом UTF-8 ToolAcre, затем декодируйте его результат и убедитесь, что тот же видимый акцент и смайлы сохранились.
Старый трюк unescape(encodeURIComponent()) и почему это хак — что он делает под капотом и почему его не рекомендуют
Исторический обходной путь — btoa(unescape(encodeURIComponent(text))). encodeURIComponent процентно кодирует UTF-8, а unescape переупаковывает тройки процентов как отдельные единицы кода, но unescape устарел, его трудно читать, и он неудобен для некорректных одиночных суррогатов. Преобразование выглядит как обработка URL, даже если URL не существует. TextEncoder четко указывает намеченную границу: текст становится байтом один раз, и только после этого работает Base64.
Декодирование с другой стороны — соединение atob с TextDecoder, чтобы передача туда и обратно проходила без потерь.
После atob не вызывайте decodeURIComponent для произвольных двоичных байтов и надейтесь, что они станут текстом. Преобразуйте коды символов в Uint8Array и передайте их через TextDecoder. В примере с кафе 😀 результатом является исходная десятибайтовая последовательность UTF-8, а затем исходная строка. Если Base64 декодирует в байты изображения или сжатого файла, он может вообще не представлять действительный текст UTF-8; ToolAcre сообщает, что вместо того, чтобы притворяться, что двоичные данные являются читаемой прозой.
Что здесь не распространяется — кодирование файлов и двоичных объектов, варианты base64url и потоковая передача больших входных данных.
Это объяснение касается текста, закодированного как UTF-8. Файлы и BLOB-объекты Base64, Base64url для сегментов JWT и инкрементальное кодирование многогигабайтных данных имеют разные интерфейсы или потребности в памяти. Base64 также не шифрует токен: любой, кто владеет им, может декодировать байты. Декодер может принимать общие недостающие дополнения и пробелы, но совместимость по-прежнему зависит от знания того, является ли полезная нагрузка текстовыми или произвольными двоичными данными.
Вывод: кодируйте байты, а не строки, и проверяйте круговой обход — как кодировщик и декодер Base64 выполняют за вас шаг UTF-8, чтобы акценты, CJK и эмодзи сохранились.
Кодируйте байты, а не необработанные строки JavaScript, а затем проверяйте обратный путь. Кодер и декодер Base64 выполняет за вас действия TextEncoder и TextDecoder, сохраняя при этом вставленный текст в браузере. RFC 4648 определяет алфавит и заполнение; UTF-8 предоставляет отдельный контракт между символами и байтами. Смешение этих двух слоев является корнем как InvalidCharacterError, так и тихого повреждения Latin-1.