Không thể thực thi 'btoa' trên 'Window': Chuỗi được mã hóa chứa các ký tự nằm ngoài phạm vi Latin1.


133

Các lỗi trong tiêu đề chỉ được ném trong Google Chrome, theo các thử nghiệm của tôi. Tôi đang mã hóa 64 tệp XML lớn để có thể tải xuống tệp:

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 được ẩn iframe.

Lỗi này thực sự là một thay đổi vì thông thường, Google Chrome sẽ gặp sự cố khi btoagọi. Mozilla Firefox không có vấn đề gì ở đây, vì vậy vấn đề liên quan đến trình duyệt. Tôi không biết bất kỳ ký tự lạ trong tập tin. Thật ra tôi tin rằng không có nhân vật không phải là ascii.

H: Làm cách nào để tìm các ký tự có vấn đề và thay thế chúng để Chrome ngừng phàn nàn?

Tôi đã thử sử dụng Downloadify để bắt đầu tải xuống, nhưng nó không hoạt động. Nó không đáng tin cậy và không có lỗi để cho phép gỡ lỗi.

Câu trả lời:


212

Nếu bạn có UTF8, hãy sử dụng cái này (thực sự hoạt động với nguồn SVG), như:

btoa(unescape(encodeURIComponent(str)))

thí dụ:

 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;

Nếu bạn cần giải mã base64 đó, hãy sử dụng:

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

Thí dụ:

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

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

Lưu ý: nếu bạn cần làm cho điều này hoạt động trong safari di động, bạn có thể cần phải loại bỏ tất cả khoảng trắng khỏi dữ liệu cơ sở64 ...

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

Cập nhật 2017

Vấn đề này đã được làm phiền tôi một lần nữa.
Sự thật đơn giản là, atob không thực sự xử lý các chuỗi UTF8 - đó chỉ là ASCII.
Ngoài ra, tôi sẽ không sử dụng bloatware như js-base64.
Nhưng webtoolkit có một triển khai nhỏ, đẹp và rất dễ bảo trì:

/**
*
*  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

  • Đối với bất kỳ ký tự nào bằng hoặc dưới 127 (hex 0x7F), biểu diễn UTF-8 là một byte. Nó chỉ là 7 bit thấp nhất của giá trị unicode đầy đủ. Giá trị này cũng giống như giá trị ASCII.

  • Đối với các ký tự bằng hoặc dưới 2047 (hex 0x07FF), biểu diễn UTF-8 được trải rộng trên hai byte. Byte đầu tiên sẽ có hai bit cao được đặt và bit thứ ba rõ ràng (nghĩa là 0xC2 đến 0xDF). Byte thứ hai sẽ có tập bit trên cùng và bit thứ hai rõ ràng (tức là 0x80 đến 0xBF).

  • Đối với tất cả các ký tự bằng hoặc lớn hơn 2048 nhưng nhỏ hơn 65535 (0xFFFF), biểu diễn UTF-8 được trải đều trên ba byte.


6
bạn có thể giải thích điều này thêm một chút nữa không ... tôi hoàn toàn lạc lối
Muhammad Umer

Tôi chỉ chạy mã nếu tôi là bạn. escapechuyển đổi chuỗi trong một chuỗi chỉ chứa các ký tự hợp lệ url. Điều đó ngăn ngừa các lỗi.
Tomáš Zato - Phục hồi Monica

6
escapeunescapekhông được dùng trong JavaScript 1.5 và thay vào đó, người ta nên sử dụng encodeURIComponenthoặc decodeURIComponentthay vào đó. Bạn đang sử dụng các chức năng không dùng nữa và mới. Tại sao? Xem: w3schools.com/jsref/jsref_escape.asp
Leif

2
@Leif: Điều này chỉ hoạt động chính xác bởi vì thoát và unescape là lỗi (theo cùng một cách);)
Stefan Steiger

8
Bất cứ ai khác vết thương ở đây từ việc sử dụng webpack?
Avindra Goolcharan 27/08/2015

18

Sử dụng btoavới unescapeencodeURIComponentkhông làm việc cho tôi. Thay thế tất cả các ký tự đặc biệt bằng các thực thể XML / HTML và sau đó chuyển đổi sang biểu diễn base64 là cách duy nhất để giải quyết vấn đề này cho tôi. Một số mã:

base64 = btoa(str.replace(/[\u00A0-\u2666]/g, function(c) {
    return '&#' + c.charCodeAt(0) + ';';
}));

1
Vì tôi đã đăng câu hỏi này, tôi đã học được một chút về các API dành riêng cho những gì tôi đang làm. Nếu chuỗi bạn chuyển đổi dài, hãy sử dụng Blobđối tượng để xử lý chuyển đổi. Blobcó thể xử lý bất kỳ dữ liệu nhị phân.
Tomáš Zato - Phục hồi Monica

1
Không chắc chắn về IE9. Nhưng tôi nghĩ rằng nếu bạn đang làm những thứ như máy khách chuyển đổi cơ sở 64, thì có lẽ bạn đang tạo ra ứng dụng web hiện đại, sớm hay muộn, sẽ cần các tính năng hiện đại. Ngoài ra, có một polyfill blob.
Tomáš Zato - Phục hồi Monica

1
@ItaloBorssatto Bạn là một huyền thoại!
codeepic

1
@ItaloBorssatto Đó là giải pháp duy nhất hiệu quả với tôi. Tôi cần nó để lấy biểu đồ d3 svg, tuần tự hóa nó bằng XMLSerializer, chuyển nó vào btoa () (đây là nơi tôi đã sử dụng giải pháp của bạn) để tạo chuỗi ASCII được mã hóa cơ sở 64, sau đó chuyển nó vào thành phần hình ảnh sau đó vẽ thành canvas và sau đó xuất nó để bạn có thể tải xuống một hình ảnh ở mặt trước. Giải pháp khá phức tạp và hacky, nhưng một giải pháp không yêu cầu biểu đồ kết xuất phía máy chủ khi người dùng muốn tải xuống một số đồ họa. Nếu bạn quan tâm tôi có thể gửi cho bạn một số mẫu mã. Nhận xét này quá ngắn đối với họ
codeepic

1
@ItaloBorssatto <svg xmlns = " w3.org/2000/svg " viewBox = "0 0 1060 105" width = "1060" height = "105"> <path class = "domain" Stro = "none" d = "M -6,0.5H0.5V35.5H-6 "> <line Stro =" none "x2 =" - 6 "y1 =" 0.5 "y2 =" 0.5 "fill =" none "Stro-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> <orth class = "thanh đầu tiên" fill = "rgb (25, 244, 71)" x = "0" y = "8" width = "790" height = "18" /> </ g> </ svg> Tôi cắt ra những mảnh không liên quan. Thủ phạm là Vogue España -> ñ đã ngăn hình ảnh tải trong trình duyệt.
codeepic

15

Sử dụng thư viện thay thế

Chúng tôi không phải phát minh lại bánh xe. Chỉ cần sử dụng một thư viện để tiết kiệm thời gian và đau đầu.

js-cơ sở64

https://github.com/dankogai/js-base64 là tốt và tôi xác nhận rằng nó hỗ trợ unicode rất tốt.

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-');  // 小飼弾

Đây là một giải pháp tốt, mặc dù có vẻ như việc giám sát btoa bị giới hạn ở ASCII (mặc dù giải mã atob có vẻ hoạt động tốt). Điều này làm việc cho tôi sau khi một số câu trả lời khác sẽ không. Cảm ơn!
Cho Tên

9

Tôi chỉ nghĩ rằng tôi nên chia sẻ cách tôi thực sự giải quyết vấn đề và tại sao tôi nghĩ đây là giải pháp phù hợp (miễn là bạn không tối ưu hóa cho trình duyệt cũ).

Chuyển đổi dữ liệu sang 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);

Cho phép người dùng lưu dữ liệu

Ngoài giải pháp rõ ràng - mở cửa sổ mới với dataURL của bạn dưới dạng URL, bạn có thể thực hiện hai việc khác.

1. Sử dụng tệpSaver.js

Trình tiết kiệm tệp có thể tạo hộp thoại FileSave thực tế với tên tệp được xác định trước. Nó cũng có thể dự phòng cho cách tiếp cận dataURL bình thường.

2. Sử dụng (thử nghiệm) URL.createObjectURL

Điều này là tuyệt vời để sử dụng lại dữ liệu được mã hóa base64. Nó tạo một URL ngắn cho dataURL của bạn:

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

Đừng quên sử dụng URL bao gồm blobtiền tố hàng đầu . Tôi đã sử dụng document.bodylại:

Mô tả hình ảnh

Bạn có thể sử dụng URL ngắn này làm mục tiêu AJAX, <script>nguồn hoặc <a>vị trí href. Bạn chịu trách nhiệm cho việc hủy URL mặc dù:

URL.revokeObjectURL('blob:http://stackoverflow.com/7c18953f-f5f8-41d2-abf5-e9cbced9bc42')

Cảm ơn bạn, bạn đã cứu ngày của tôi :)
Sandeep Kumar

3

Như một bổ sung cho câu trả lời của Stefan Steiger: (vì nó không đẹp như bình luận)

Mở rộng nguyên mẫu Chuỗi:

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

Sử dụng:

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

GHI CHÚ:

Như đã nêu trong các ý kiến, việc sử dụng unescapekhông được khuyến khích vì nó có thể bị xóa trong tương lai:

Cảnh báo : Mặc dù unescape () không bị phản đối nghiêm ngặt (như trong "đã bị xóa khỏi các tiêu chuẩn Web"), nhưng nó được định nghĩa trong Phụ lục B của tiêu chuẩn ECMA-262, có phần giới thiệu: tất cả các tính năng và hành vi ngôn ngữ được chỉ định trong phần này phụ lục có một hoặc nhiều đặc điểm không mong muốn và trong trường hợp không sử dụng di sản sẽ bị xóa khỏi đặc điểm kỹ thuật này.

Lưu ý: Không sử dụng unescape để giải mã URI, thay vào đó hãy sử dụng decodeURI hoặc decodeURIComponent .


6
Các chức năng có vẻ tốt, nhưng mở rộng các nguyên mẫu cơ sở là một thực tiễn xấu.
timemachine3030

4
Javascript là một thực hành xấu. Thêm một lần hack nữa, cảm ơn.
rob5408

1
@ rob5408: Mặc dù tôi đồng ý với tuyên bố của bạn về nguyên tắc, nhưng bạn thực sự nên thận trọng hơn: Việc mở rộng các nguyên mẫu phá vỡ jQuery (một thư viện khác sử dụng nguyên tắc "chỉ một lần hack nữa")
Stefan Steiger

@StefanSteiger Rất tốt để biết, cảm ơn vì sự sáng suốt.
rob5408


2

btoa () chỉ hỗ trợ các ký tự từ String.fromCodePoint (0) cho đến String.fromCodePoint (255). Đối với các ký tự Base64 có mã điểm 256 trở lên, bạn cần mã hóa / giải mã các ký tự này trước và sau.

Và trong thời điểm này, nó trở nên khó khăn ...

Mọi dấu hiệu có thể được sắp xếp trong Bảng Unicode. Bảng Unicode được chia thành các mặt phẳng khác nhau (ngôn ngữ, ký hiệu toán học, v.v.). Mỗi dấu hiệu trong một mặt phẳng có một số điểm mã duy nhất. Về mặt lý thuyết, số lượng có thể trở nên lớn tùy ý.

Một máy tính lưu trữ dữ liệu theo byte (8 bit, thập lục phân 0x00 - 0xff, nhị phân 00000000 - 11111111, thập phân 0 - 255). Phạm vi này thường được sử dụng để lưu các ký tự cơ bản (phạm vi Latin1).

Đối với các ký tự có mật mã cao hơn thì 255 tồn tại các bảng mã khác nhau. JavaScript sử dụng 16 bit cho mỗi dấu (UTF-16), chuỗi có tên DOMString. Unicode có thể xử lý các điểm mã lên đến 0x10fffff. Điều đó có nghĩa là, một phương thức phải tồn tại để lưu trữ một số bit trên một số ô.

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

UTF-16 sử dụng các cặp thay thế để lưu trữ 20 bit trong hai ô 16 bit. Người thay thế cao hơn đầu tiên bắt đầu bằng 110110xxxxxxxxxx , người thứ hai thấp hơn với 110111xxxxxxxxxx . Unicode dành riêng cho các mặt phẳng này: https://unicode-table.com/de/#high-surrogates

Để lưu trữ các ký tự theo byte (phạm vi Latin1), hãy sử dụng UTF-8 .

Xin lỗi để nói điều đó, nhưng tôi nghĩ không có cách nào khác để tự thực hiện chức năng này.

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;

Làm thế nào để sử dụng nó: decodeBase64(encodeBase64("\u{1F604}"))

bản demo: https://jsfiddle.net/qrLadeb8/


Hoạt động tuyệt vời! Tôi không thấy nơi bạn cần stringToUTF8utf8ToStringmặc dù
Benjamin Toueg

1

Tôi chỉ gặp vấn đề này bản thân mình.

Đầu tiên, sửa đổi mã của bạn một chút:

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

Sau đó, sử dụng trình kiểm tra web yêu thích của bạn, đặt một điểm dừng trên dòng mã gán this.loader.src, sau đó thực thi mã này:

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

Tùy thuộc vào ứng dụng của bạn, việc thay thế các ký tự nằm ngoài phạm vi có thể hoặc không thể hoạt động, vì bạn sẽ sửa đổi dữ liệu. Xem ghi chú trên MDN về các ký tự unicode bằng phương thức btoa:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.