Не удалось выполнить 'btoa' в 'Window': кодируемая строка содержит символы за пределами диапазона Latin1.

133

По моим тестам ошибка в заголовке выкидывается только в Google Chrome. Я кодирую base64 большой XML-файл, чтобы его можно было загрузить:

this.loader.src = "data:application/x-forcedownload;base64,"+
                  btoa("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                  +"<"+this.gamesave.tagName+">"
                  +this.xml.firstChild.innerHTML
                  +"</"+this.gamesave.tagName+">");

this.loader скрыт iframe.

Эта ошибка на самом деле является серьезным изменением, потому что обычно Google Chrome вылетает при btoaвызове. У Mozilla Firefox здесь нет проблем, поэтому проблема связана с браузером. Мне неизвестны какие-либо странные символы в файле. На самом деле я считаю, что символов, отличных от ascii, нет.

В: Как найти проблемные символы и заменить их, чтобы Chrome перестал жаловаться?

Я попытался использовать Downloadify, чтобы начать загрузку, но это не сработало. Это ненадежно и не выдает ошибок, чтобы разрешить отладку.

Томаш Зато - Восстановить Монику
источник

Ответы:

213

Если у вас UTF8, используйте это (на самом деле работает с исходным кодом SVG), например:

btoa(unescape(encodeURIComponent(str)))

пример:

 var imgsrc = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(markup)));
 var img = new Image(1, 1); // width, height values are optional params 
 img.src = imgsrc;

Если вам нужно декодировать этот base64, используйте это:

var str2 = decodeURIComponent(escape(window.atob(b64)));
console.log(str2);

Пример:

var str = "äöüÄÖÜçéèñ";
var b64 = window.btoa(unescape(encodeURIComponent(str)))
console.log(b64);

var str2 = decodeURIComponent(escape(window.atob(b64)));
console.log(str2);

Примечание: если вам нужно заставить это работать в mobile-safari, вам может потребоваться удалить все пробелы из данных base64 ...

function b64_to_utf8( str ) {
    str = str.replace(/\s/g, '');    
    return decodeURIComponent(escape(window.atob( str )));
}

Обновление 2017 г.

Эта проблема снова меня беспокоит.
Простая правда в том, что atob на самом деле не обрабатывает строки UTF8 - это только ASCII.
Кроме того, я бы не стал использовать раздутое ПО вроде js-base64.
Но у webtoolkit есть небольшая, красивая и очень удобная в обслуживании реализация:

/**
*
*  Base64 encode / decode
*  http://www.webtoolkit.info
*
**/
var Base64 = {

    // private property
    _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

    // public method for encoding
    , encode: function (input)
    {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;

        input = Base64._utf8_encode(input);

        while (i < input.length)
        {
            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2))
            {
                enc3 = enc4 = 64;
            }
            else if (isNaN(chr3))
            {
                enc4 = 64;
            }

            output = output +
                this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
                this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
        } // Whend 

        return output;
    } // End Function encode 


    // public method for decoding
    ,decode: function (input)
    {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
        while (i < input.length)
        {
            enc1 = this._keyStr.indexOf(input.charAt(i++));
            enc2 = this._keyStr.indexOf(input.charAt(i++));
            enc3 = this._keyStr.indexOf(input.charAt(i++));
            enc4 = this._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64)
            {
                output = output + String.fromCharCode(chr2);
            }

            if (enc4 != 64)
            {
                output = output + String.fromCharCode(chr3);
            }

        } // Whend 

        output = Base64._utf8_decode(output);

        return output;
    } // End Function decode 


    // private method for UTF-8 encoding
    ,_utf8_encode: function (string)
    {
        var utftext = "";
        string = string.replace(/\r\n/g, "\n");

        for (var n = 0; n < string.length; n++)
        {
            var c = string.charCodeAt(n);

            if (c < 128)
            {
                utftext += String.fromCharCode(c);
            }
            else if ((c > 127) && (c < 2048))
            {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else
            {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        } // Next n 

        return utftext;
    } // End Function _utf8_encode 

    // private method for UTF-8 decoding
    ,_utf8_decode: function (utftext)
    {
        var string = "";
        var i = 0;
        var c, c1, c2, c3;
        c = c1 = c2 = 0;

        while (i < utftext.length)
        {
            c = utftext.charCodeAt(i);

            if (c < 128)
            {
                string += String.fromCharCode(c);
                i++;
            }
            else if ((c > 191) && (c < 224))
            {
                c2 = utftext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else
            {
                c2 = utftext.charCodeAt(i + 1);
                c3 = utftext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }

        } // Whend 

        return string;
    } // End Function _utf8_decode 

}

https://www.fileformat.info/info/unicode/utf8.htm

  • Для любого символа, равного или меньше 127 (шестнадцатеричный 0x7F), представление UTF-8 составляет один байт. Это всего лишь младшие 7 бит полного значения Unicode. Это также то же самое, что и значение ASCII.

  • Для символов, равных или меньше 2047 (шестнадцатеричный 0x07FF), представление UTF-8 распространяется на два байта. В первом байте будут установлены два старших бита, а в третьем будут сброшены (то есть от 0xC2 до 0xDF). Во втором байте будет установлен верхний бит, а второй бит будет очищен (т.е. от 0x80 до 0xBF).

  • Для всех символов, равных или больше 2048, но меньше 65535 (0xFFFF), представление UTF-8 распространяется на три байта.

Стефан Штайгер
источник
6
Можете ли вы объяснить это еще немного ... Я полностью потерян
Мухаммад Умер
Я бы просто запустил код на твоем месте. escapeпреобразует строку в строку, содержащую только допустимые символы URL. Это предотвращает ошибки.
Tomáš Zato - Reinstate Monica
6
escapeи unescapeбыли объявлены устаревшими в JavaScript 1.5, и вместо них следует использовать encodeURIComponentили decodeURIComponent, соответственно. Вы одновременно используете устаревшие и новые функции. Зачем? См .: w3schools.com/jsref/jsref_escape.asp
Лейф,
2
@Leif: Это работает только потому, что escape и unescape ошибочны (точно так же);)
Stefan Steiger
8
Кто-нибудь еще попал сюда из-за использования веб-пакета?
Авиндра Гулчаран,
18

Использование btoawith unescapeи encodeURIComponentне помогло мне. Замена всех специальных символов сущностями XML / HTML и последующее преобразование в представление base64 было для меня единственным способом решить эту проблему. Некоторый код:

base64 = btoa(str.replace(/[\u00A0-\u2666]/g, function(c) {
    return '&#' + c.charCodeAt(0) + ';';
}));
Итало Борссатто
источник
1
С тех пор, как я разместил этот вопрос, я немного узнал об API, предназначенных для моей работы. Если преобразуемая строка длинная, используйте Blobобъект для обработки преобразования. Blobможет обрабатывать любые двоичные данные.
Томаш Зато - Восстановите Монику
1
Не уверен насчет IE9. Но я думаю, что если вы делаете что-то вроде преобразования на стороне клиента base64, вы, вероятно, создаете современное веб-приложение, которому рано или поздно все равно потребуются современные функции. Также есть полифил blob.
Томаш Зато - Восстановите Монику
1
@ItaloBorssatto Ты легенда!
codeepic
1
@ItaloBorssatto Это было единственное решение, которое сработало для меня. Мне это нужно, чтобы получить диаграмму svg d3, сериализовать ее с помощью XMLSerializer, передать ее в btoa () (здесь я использовал ваше решение), чтобы создать строку ASCII в кодировке base-64, а затем передать ее в элемент изображения, который является затем нарисовать на холсте, а затем экспортировать его, чтобы вы могли загрузить изображение на передней панели. Довольно запутанное и хакерское решение, но оно не требует отображения диаграмм на стороне сервера, когда пользователи хотят загрузить некоторую графику. Если вам интересно, могу выслать вам несколько примеров кода. Комментарий слишком короткий для них
codeepic
1
@ItaloBorssatto <svg xmlns = " w3.org/2000/svg " viewBox = "0 0 1060 105" width = "1060" height = "105"> <path class = "domain" stroke = "none" d = "M -6,0.5H0.5V35.5H-6 "> <line stroke =" none "x2 =" - 6 "y1 =" 0.5 "y2 =" 0.5 "fill =" none "stroke-width =" 1px "font- family = "sans-serif" font-size = "10px" /> <text fill = "rgb (196, 196, 196)" x = "- 9" y = "0.5" dy = "0.32em"> VogueEspana - Vogue España </text> <rect class = "first bar" fill = "rgb (25, 244, 71)" x = "0" y = "8" width = "790" height = "18" /> </ g> </svg> Я вырезал ненужные части. Виновник - Vogue España -> ñ предотвратил загрузку изображения в браузере.
codeepic
15

Вместо этого используйте библиотеку

Нам не нужно изобретать велосипед. Просто используйте библиотеку, чтобы сэкономить время и избавиться от головной боли.

JS-base64

https://github.com/dankogai/js-base64 - это хорошо, и я подтверждаю, что он очень хорошо поддерживает юникод.

Base64.encode('dankogai');  // ZGFua29nYWk=
Base64.encode('小飼弾');    // 5bCP6aO85by+
Base64.encodeURI('小飼弾'); // 5bCP6aO85by-

Base64.decode('ZGFua29nYWk=');  // dankogai
Base64.decode('5bCP6aO85by+');  // 小飼弾
// note .decodeURI() is unnecessary since it accepts both flavors
Base64.decode('5bCP6aO85by-');  // 小飼弾
Тайлер Лонг
источник
Это хорошее решение, хотя кажется, что ограничение btoa до ASCII является упущением (хотя декодирование atob, похоже, работает нормально). Это сработало для меня после того, как несколько других ответов не помогли. Спасибо!
For the Name
9

Я просто подумал, что должен рассказать, как я на самом деле решил проблему и почему считаю это правильным решением (при условии, что вы не оптимизируете его для старого браузера).

Преобразование данных в dataURL ( data: ...)

var blob = new Blob(
              // I'm using page innerHTML as data
              // note that you can use the array
              // to concatenate many long strings EFFICIENTLY
              [document.body.innerHTML],
              // Mime type is important for data url
              {type : 'text/html'}
); 
// This FileReader works asynchronously, so it doesn't lag
// the web application
var a = new FileReader();
a.onload = function(e) {
     // Capture result here
     console.log(e.target.result);
};
a.readAsDataURL(blob);

Разрешение пользователю сохранять данные

Помимо очевидного решения - открытия нового окна с вашим dataURL в качестве URL-адреса, вы можете сделать еще две вещи.

1. Используйте fileSaver.js

Хранитель файлов может создать фактический диалог сохранения файла с предопределенным именем файла. Он также может вернуться к обычному подходу dataURL.

2. Использование (экспериментальное) URL.createObjectURL

Это отлично подходит для повторного использования данных в кодировке base64. Он создает короткий URL-адрес для вашего dataURL:

console.log(URL.createObjectURL(blob));
//Prints: blob:http://stackoverflow.com/7c18953f-f5f8-41d2-abf5-e9cbced9bc42

Не забудьте использовать URL-адрес, включая начальный blobпрефикс. Я document.bodyснова использовал :

Описание изображения

Вы можете использовать этот короткий URL-адрес в качестве цели AJAX, <script>источника или <a>местоположения href. Однако вы несете ответственность за уничтожение URL:

URL.revokeObjectURL('blob:http://stackoverflow.com/7c18953f-f5f8-41d2-abf5-e9cbced9bc42')
Томаш Зато - Восстановить Монику
источник
Спасибо, дружище, ты спас мне день :)
Сандип Кумар
3

В качестве дополнения к ответу Стефана Штайгера: (поскольку это не выглядит красиво в качестве комментария)

Расширение прототипа String:

String.prototype.b64encode = function() { 
    return btoa(unescape(encodeURIComponent(this))); 
};
String.prototype.b64decode = function() { 
    return decodeURIComponent(escape(atob(this))); 
};

Использование:

var str = "äöüÄÖÜçéèñ";
var encoded = str.b64encode();
console.log( encoded.b64decode() );

НОТА:

Как указано в комментариях, использование unescapeне рекомендуется, так как оно может быть удалено в будущем:

Предупреждение : хотя unescape () не является строго устаревшим (например, «удален из веб-стандартов»), он определен в Приложении B стандарта ECMA-262, введение которого гласит:… Все языковые функции и поведение, указанные в этом Приложение имеет одну или несколько нежелательных характеристик и в случае отсутствия устаревшего использования будет удалено из данной спецификации.

Примечание. Не используйте unescape для декодирования URI, вместо этого используйте decodeURI или decodeURIComponent .

Лепе
источник
6
Функции выглядят хорошо, но расширение базовых прототипов - плохая практика.
timemachine3030,
4
Javascript - плохая практика. Еще один взлом, спасибо.
rob5408 03
1
@ rob5408: Хотя я в принципе согласен с вашим утверждением, но вам следует быть более осторожным: расширение прототипов нарушает jQuery (еще одна библиотека, которая использует принцип «еще один взлом»)
Стефан Штайгер
@StefanSteiger Полезно знать, спасибо за понимание.
rob5408
unescapeскоро будет устаревшим согласно MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Akansh
2

btoa () поддерживает только символы от String.fromCodePoint (0) до String.fromCodePoint (255). Для символов Base64 с кодовой точкой 256 или выше вам необходимо кодировать / декодировать их до и после.

И тут становится сложно ...

Все возможные знаки расположены в таблице Unicode. Таблица Unicode разделена на разные плоскости (языки, математические символы и так далее ...). Каждый знак в самолете имеет уникальный номер кодовой точки. Теоретически количество может стать сколь угодно большим.

Компьютер хранит данные в байтах (8 бит, шестнадцатеричный 0x00 - 0xff, двоичный 00000000 - 11111111, десятичный 0-255). Этот диапазон обычно используется для сохранения основных символов (диапазон Latin1).

Для символов с более высоким кодом существуют 255 различных кодировок. В JavaScript используется 16 бит на знак (UTF-16), строка называется DOMString. Unicode может обрабатывать кодовые точки до 0x10fffff. Это означает, что должен существовать метод для хранения нескольких бит на расстоянии нескольких ячеек.

String.fromCodePoint(0x10000).length == 2

UTF-16 использует суррогатные пары для хранения 20 бит в двух 16-битных ячейках. Первый более высокий суррогат начинается с 110110xxxxxxxxxx , младший второй - с 110111xxxxxxxxxx . Unicode зарезервировал для этого собственные самолеты: https://unicode-table.com/de/#high-surrogates

Для хранения символов в байтах (диапазон Latin1) стандартные процедуры используют UTF-8 .

Извините за это, но я думаю, что другого способа реализовать эту функцию самостоятельно нет.

function stringToUTF8(str)
{
    let bytes = [];

    for(let character of str)
    {
        let code = character.codePointAt(0);

        if(code <= 127)
        {
            let byte1 = code;

            bytes.push(byte1);
        }
        else if(code <= 2047)
        {
            let byte1 = 0xC0 | (code >> 6);
            let byte2 = 0x80 | (code & 0x3F);

            bytes.push(byte1, byte2);
        }
        else if(code <= 65535)
        {
            let byte1 = 0xE0 | (code >> 12);
            let byte2 = 0x80 | ((code >> 6) & 0x3F);
            let byte3 = 0x80 | (code & 0x3F);

            bytes.push(byte1, byte2, byte3);
        }
        else if(code <= 2097151)
        {
            let byte1 = 0xF0 | (code >> 18);
            let byte2 = 0x80 | ((code >> 12) & 0x3F);
            let byte3 = 0x80 | ((code >> 6) & 0x3F);
            let byte4 = 0x80 | (code & 0x3F);

            bytes.push(byte1, byte2, byte3, byte4);
        }
    }

    return bytes;
}

function utf8ToString(bytes, fallback)
{
    let valid = undefined;
    let codePoint = undefined;
    let codeBlocks = [0, 0, 0, 0];

    let result = "";

    for(let offset = 0; offset < bytes.length; offset++)
    {
        let byte = bytes[offset];

        if((byte & 0x80) == 0x00)
        {
            codeBlocks[0] = byte & 0x7F;

            codePoint = codeBlocks[0];
        }
        else if((byte & 0xE0) == 0xC0)
        {
            codeBlocks[0] = byte & 0x1F;

            byte = bytes[++offset];
            if(offset >= bytes.length || (byte & 0xC0) != 0x80) { valid = false; break; }

            codeBlocks[1] = byte & 0x3F;

            codePoint = (codeBlocks[0] << 6) + codeBlocks[1];
        }
        else if((byte & 0xF0) == 0xE0)
        {
            codeBlocks[0] = byte & 0xF;

            for(let blockIndex = 1; blockIndex <= 2; blockIndex++)
            {
                byte = bytes[++offset];
                if(offset >= bytes.length || (byte & 0xC0) != 0x80) { valid = false; break; }

                codeBlocks[blockIndex] = byte & 0x3F;
            }
            if(valid === false) { break; }

            codePoint = (codeBlocks[0] << 12) + (codeBlocks[1] << 6) + codeBlocks[2];
        }
        else if((byte & 0xF8) == 0xF0)
        {
            codeBlocks[0] = byte & 0x7;

            for(let blockIndex = 1; blockIndex <= 3; blockIndex++)
            {
                byte = bytes[++offset];
                if(offset >= bytes.length || (byte & 0xC0) != 0x80) { valid = false; break; }

                codeBlocks[blockIndex] = byte & 0x3F;
            }
            if(valid === false) { break; }

            codePoint = (codeBlocks[0] << 18) + (codeBlocks[1] << 12) + (codeBlocks[2] << 6) + (codeBlocks[3]);
        }
        else
        {
            valid = false; break;
        }

        result += String.fromCodePoint(codePoint);
    }

    if(valid === false)
    {
        if(!fallback)
        {
            throw new TypeError("Malformed utf-8 encoding.");
        }

        result = "";

        for(let offset = 0; offset != bytes.length; offset++)
        {
            result += String.fromCharCode(bytes[offset] & 0xFF);
        }
    }

    return result;
}

function decodeBase64(text, binary)
{
    if(/[^0-9a-zA-Z\+\/\=]/.test(text)) { throw new TypeError("The string to be decoded contains characters outside of the valid base64 range."); }

    let codePointA = 'A'.codePointAt(0);
    let codePointZ = 'Z'.codePointAt(0);
    let codePointa = 'a'.codePointAt(0);
    let codePointz = 'z'.codePointAt(0);
    let codePointZero = '0'.codePointAt(0);
    let codePointNine = '9'.codePointAt(0);
    let codePointPlus = '+'.codePointAt(0);
    let codePointSlash = '/'.codePointAt(0);

    function getCodeFromKey(key)
    {
        let keyCode = key.codePointAt(0);

        if(keyCode >= codePointA && keyCode <= codePointZ)
        {
            return keyCode - codePointA;
        }
        else if(keyCode >= codePointa && keyCode <= codePointz)
        {
            return keyCode + 26 - codePointa;
        }
        else if(keyCode >= codePointZero && keyCode <= codePointNine)
        {
            return keyCode + 52 - codePointZero;
        }
        else if(keyCode == codePointPlus)
        {
            return 62;
        }
        else if(keyCode == codePointSlash)
        {
            return 63;
        }

        return undefined;
    }

    let codes = Array.from(text).map(character => getCodeFromKey(character));

    let bytesLength = Math.ceil(codes.length / 4) * 3;

    if(codes[codes.length - 2] == undefined) { bytesLength = bytesLength - 2; } else if(codes[codes.length - 1] == undefined) { bytesLength--; }

    let bytes = new Uint8Array(bytesLength);

    for(let offset = 0, index = 0; offset < bytes.length;)
    {
        let code1 = codes[index++];
        let code2 = codes[index++];
        let code3 = codes[index++];
        let code4 = codes[index++];

        let byte1 = (code1 << 2) | (code2 >> 4);
        let byte2 = ((code2 & 0xf) << 4) | (code3 >> 2);
        let byte3 = ((code3 & 0x3) << 6) | code4;

        bytes[offset++] = byte1;
        bytes[offset++] = byte2;
        bytes[offset++] = byte3;
    }

    if(binary) { return bytes; }

    return utf8ToString(bytes, true);
}

function encodeBase64(bytes) {
    if (bytes === undefined || bytes === null) {
        return '';
    }
    if (bytes instanceof Array) {
        bytes = bytes.filter(item => {
            return Number.isFinite(item) && item >= 0 && item <= 255;
        });
    }

    if (
        !(
            bytes instanceof Uint8Array ||
            bytes instanceof Uint8ClampedArray ||
            bytes instanceof Array
        )
    ) {
        if (typeof bytes === 'string') {
            const str = bytes;
            bytes = Array.from(unescape(encodeURIComponent(str))).map(ch =>
                ch.codePointAt(0)
            );
        } else {
            throw new TypeError('bytes must be of type Uint8Array or String.');
        }
    }

    const keys = [
        'A',
        'B',
        'C',
        'D',
        'E',
        'F',
        'G',
        'H',
        'I',
        'J',
        'K',
        'L',
        'M',
        'N',
        'O',
        'P',
        'Q',
        'R',
        'S',
        'T',
        'U',
        'V',
        'W',
        'X',
        'Y',
        'Z',
        'a',
        'b',
        'c',
        'd',
        'e',
        'f',
        'g',
        'h',
        'i',
        'j',
        'k',
        'l',
        'm',
        'n',
        'o',
        'p',
        'q',
        'r',
        's',
        't',
        'u',
        'v',
        'w',
        'x',
        'y',
        'z',
        '0',
        '1',
        '2',
        '3',
        '4',
        '5',
        '6',
        '7',
        '8',
        '9',
        '+',
        '/'
    ];
    const fillKey = '=';

    let byte1;
    let byte2;
    let byte3;
    let sign1 = ' ';
    let sign2 = ' ';
    let sign3 = ' ';
    let sign4 = ' ';

    let result = '';

    for (let index = 0; index < bytes.length; ) {
        let fillUpAt = 0;

        // tslint:disable:no-increment-decrement
        byte1 = bytes[index++];
        byte2 = bytes[index++];
        byte3 = bytes[index++];

        if (byte2 === undefined) {
            byte2 = 0;
            fillUpAt = 2;
        }

        if (byte3 === undefined) {
            byte3 = 0;
            if (!fillUpAt) {
                fillUpAt = 3;
            }
        }

        // tslint:disable:no-bitwise
        sign1 = keys[byte1 >> 2];
        sign2 = keys[((byte1 & 0x3) << 4) + (byte2 >> 4)];
        sign3 = keys[((byte2 & 0xf) << 2) + (byte3 >> 6)];
        sign4 = keys[byte3 & 0x3f];

        if (fillUpAt > 0) {
            if (fillUpAt <= 2) {
                sign3 = fillKey;
            }
            if (fillUpAt <= 3) {
                sign4 = fillKey;
            }
        }

        result += sign1 + sign2 + sign3 + sign4;

        if (fillUpAt) {
            break;
        }
    }

    return result;
}

let base64 = encodeBase64("\u{1F604}"); // unicode code point escapes for smiley
let str = decodeBase64(base64);

console.log("base64", base64);
console.log("str", str);

document.body.innerText = str;

как это использовать: decodeBase64(encodeBase64("\u{1F604}"))

демо: https://jsfiddle.net/qrLadeb8/

Мартин Вантке
источник
Прекрасно работает! 🎉 Я не вижу, где вам нужно, stringToUTF8и utf8ToStringхотя
Benjamin Toueg
1

Я сам столкнулся с этой проблемой.

Во-первых, немного измените свой код:

var download = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                  +"<"+this.gamesave.tagName+">"
                  +this.xml.firstChild.innerHTML
                  +"</"+this.gamesave.tagName+">";

this.loader.src = "data:application/x-forcedownload;base64,"+
                  btoa(download);

Затем используйте свой любимый веб-инспектор, установите точку останова в строке кода, которая назначает this.loader.src, а затем выполните этот код:

for (var i = 0; i < download.length; i++) {
  if (download[i].charCodeAt(0) > 255) {
    console.warn('found character ' + download[i].charCodeAt(0) + ' "' + download[i] + '" at position ' + i);
  }
}

В зависимости от вашего приложения замена символов, выходящих за пределы допустимого диапазона, может работать, а может и не работать, поскольку вы будете изменять данные. См. Примечание к MDN о символах Юникода с методом btoa:

https://developer.mozilla.org/en-US/docs/Web/API/window.btoa

Марк Солсбери
источник