Не вдалося виконати "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 прихований кадр.

Ця помилка насправді є суттєвою зміною, оскільки звичайно Google Chrome може вийти з ладу під час btoaвиклику. У Mozilla Firefox тут немає проблем, тому проблема пов’язана з браузером. Мені невідомі якісь дивні символи у файлі. Насправді я вважаю, що немає персонажів, які не мають права.

Питання: Як знайти проблемні символи та замінити їх, щоб 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;

Якщо вам потрібно розшифрувати базу64, скористайтеся цим:

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);

Примітка: якщо вам потрібно зробити це для роботи в мобільному сафарі, вам може знадобитися зняти весь пробіл з даних 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-адреси. Це запобігає помилкам.
Томаш Зато - Відновіть Моніку

6
escapeі unescapeбули застарілими в JavaScript 1.5, і слід використовувати encodeURIComponentабо decodeURIComponent, відповідно, замість цього. Ви разом використовуєте застарілі та нові функції. Чому? Дивіться: w3schools.com/jsref/jsref_escape.asp
Leif

2
@Leif: Це працює лише саме тому, що втеча та невідображення є баггі (однаково);)
Стефан Штайгер

8
Хто-небудь ще завершився тут від використання webpack?
Avindra Goolcharan

18

Використання btoaз 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, ви, ймовірно, робите сучасний веб-додаток, який рано чи пізно потребуватиме сучасних функцій. Крім того, є блобін поліфіл.
Томаш Зато - Відновити Моніку

1
@ItaloBorssatto Ти легенда!
codeepic

1
@ItaloBorssatto Це єдине рішення, яке працювало на мене. Мені це було потрібно для того, щоб схопити діаграму svg d3, серіалізувати її за допомогою XMLSerializer, передати її в btoa () (саме тут я використав ваше рішення), щоб створити ASCII-кодовану базу-64, кодовану базою-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 "> <рядок обведення =" немає "x2 =" - 6 "y1 =" 0,5 "y2 =" 0,5 "fill =" none "штрих-ширина =" 1px "шрифт- 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 - це добре, і я підтверджую, що він дуже добре підтримує unicode.

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 (хоча декодування атобів, здається, працює добре). Це спрацювало для мене після того, як кілька інших відповідей не відповіли б. Дякую!
Для імені

9

Я просто думав, що повинен поділитися тим, як я насправді вирішив проблему, і чому я вважаю, що це правильно рішення (за умови, що ви не оптимізуєте старий браузер).

Перетворення даних у даніURL ( 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);

Дозвіл користувача зберігати дані

Крім очевидного рішення - відкривши нове вікно з вашими данимиURL як URL, ви можете зробити ще дві речі.

1. Використовуйте fileSaver.js

Засіб зберігання файлів може створити власне діалогове вікно збереження файлів із заздалегідь визначеним іменем файлу. Він також може відновлюватись до звичайного підходу до данихURL.

2. Використання (експериментальне) URL.createObjectURL

Це чудово для повторного використання кодованих даних base64. Він створює коротку URL-адресу для ваших данихURL:

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

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хоча
Бенджамін Туег

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.