В Mozilla MDN есть замечательная статья , которая описывает именно эту проблему:
«Проблема Unicode» Поскольку DOMStrings - это строки с 16-битным кодированием, в большинстве браузеры, вызывающие window.btoa в строке Unicode, вызывают исключение Character Out Of Range, если символ превышает диапазон 8-разрядного символа в кодировке ASCII. Существует два возможных способа решения этой проблемы:
- первым из них является выход из всей строки, а затем ее кодирование,
- , второй - преобразование UTF- 16 DOMString в массив символов UTF-8 и затем кодировать его.
Примечание к исходному ответу: ранее в статье MDN было предложено использовать
unescape
иescape
для решения проблемы исключенияCharacter Out Of Range
, но с тех пор они устарели. Некоторые другие ответы здесь предложили обойти это с помощьюdecodeURIComponent
иencodeURIComponent
, это оказалось ненадежным и непредсказуемым.В конце концов, вы определенно могли бы сэкономить некоторое горе с помощью библиотеки:
Вот текущая рекомендация, прямо из MDN, с некоторой дополнительной совместимостью TypeScript через @ MA-Maddin:
Кодирование UTF8 ⇢ base64
Реализовать регулярное выражение вместо устаревшей функции unescape
function b64EncodeUnicode(str) { return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) { return String.fromCharCode(parseInt(p1, 16)) })) } b64EncodeUnicode('✓ à la mode') // "4pyTIMOgIGxhIG1vZGU=" b64EncodeUnicode('\n') // "Cg=="
Декодирование base64 ⇢ UTF8
В статье MDN изначально не был пример для декодирования, но теперь добавлено
function b64DecodeUnicode(str) { return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2) }).join('')) } b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU=') // "✓ à la mode" b64DecodeUnicode('Cg==') // "\n"
Исходное решение (устарело)
Это использовало
escape
иunescape
(которые теперь устарели, хотя это все еще работает во всех современных браузерах):function utf8_to_b64( str ) { return window.btoa(unescape(encodeURIComponent( str ))); } function b64_to_utf8( str ) { return decodeURIComponent(escape(window.atob( str ))); } // Usage: utf8_to_b64('✓ à la mode'); // "4pyTIMOgIGxhIG1vZGU=" b64_to_utf8('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"
И последнее: впервые я столкнулся с этой проблемой при вызове API GitHub. Чтобы заставить это работать на (Mobile) Safari должным образом, я фактически должен был удалить все пустое пространство от источника base64 до , я мог бы даже декодировать источник. Является ли это еще актуальным в 2017 году, я не знаю:
function b64_to_utf8( str ) { str = str.replace(/\s/g, ''); return decodeURIComponent(escape(window.atob( str ))); }