Menggunakan atob Javascript untuk mendekode base64 tidak mendekode string utf-8 dengan benar

106

Saya menggunakan window.atob()fungsi Javascript untuk mendekode string berenkode base64 (khususnya konten berenkode base64 dari GitHub API). Masalahnya adalah saya mendapatkan kembali karakter yang dikodekan ASCII (seperti â¢alih - alih ). Bagaimana cara saya menangani aliran berenkode base64 dengan benar sehingga diterjemahkan sebagai utf-8?

brandonscript
sumber
3
Halaman MDN yang Anda tautkan memiliki paragraf yang diawali dengan frasa "Untuk digunakan dengan string Unicode atau UTF-8".
Pointy
1
Apakah Anda berada di node? Ada solusi yang lebih baik daripadaatob
Bergi

Jawaban:

269

Ada artikel bagus di dokumen MDN Mozilla yang menjelaskan dengan tepat masalah ini:

"Masalah Unicode" Karena DOMStrings adalah string yang dikodekan 16-bit, di sebagian besar browser yang memanggil window.btoastring Unicode akan menyebabkan a Character Out Of Range exceptionjika karakter melebihi kisaran byte 8-bit (0x00 ~ 0xFF). Ada dua metode yang mungkin untuk mengatasi masalah ini:

  • yang pertama adalah melepaskan seluruh string (dengan UTF-8, lihat encodeURIComponent) dan kemudian menyandikannya;
  • yang kedua adalah mengubah UTF-16 DOMStringmenjadi array karakter UTF-8 dan kemudian menyandikannya.

Catatan tentang solusi sebelumnya: artikel MDN awalnya menyarankan penggunaan unescapedan escapeuntuk memecahkan Character Out Of Rangemasalah pengecualian, tetapi sejak itu sudah tidak digunakan lagi. Beberapa jawaban lain di sini menyarankan untuk mengatasi masalah ini dengan decodeURIComponentdan encodeURIComponent, ini terbukti tidak dapat diandalkan dan tidak dapat diprediksi. Pembaruan terbaru untuk jawaban ini menggunakan fungsi JavaScript modern untuk meningkatkan kecepatan dan memodernisasi kode.

Jika Anda mencoba menghemat waktu, Anda juga dapat mempertimbangkan untuk menggunakan perpustakaan:

Mengenkode UTF8 ⇢ base64

function b64EncodeUnicode(str) {
    // first we use encodeURIComponent to get percent-encoded UTF-8,
    // then we convert the percent encodings into raw bytes which
    // can be fed into btoa.
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
        function toSolidBytes(match, p1) {
            return String.fromCharCode('0x' + p1);
    }));
}

b64EncodeUnicode('✓ à la mode'); // "4pyTIMOgIGxhIG1vZGU="
b64EncodeUnicode('\n'); // "Cg=="

Mendekode base64 ⇢ UTF8

function b64DecodeUnicode(str) {
    // Going backwards: from bytestream, to percent-encoding, to original string.
    return decodeURIComponent(atob(str).split('').map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}

b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"
b64DecodeUnicode('Cg=='); // "\n"

Solusi pra-2018 (berfungsi, dan meskipun mungkin dukungan yang lebih baik untuk browser lama, bukan yang terbaru)

Berikut adalah rekomendasi saat ini, langsung dari MDN, dengan beberapa kompatibilitas TypeScript tambahan melalui @ MA-Maddin:

// Encoding UTF8 ⇢ base64

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=="

// Decoding base64 ⇢ UTF8

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"

Solusi asli (tidak digunakan lagi)

Ini bekas escapedan unescape(yang sekarang sudah tidak digunakan lagi, meskipun masih berfungsi di semua browser modern):

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"

Dan satu hal terakhir: Saya pertama kali mengalami masalah ini saat memanggil GitHub API. Agar ini berfungsi di Safari (Seluler) dengan benar, saya sebenarnya harus menghapus semua ruang putih dari sumber base64 bahkan sebelum saya dapat memecahkan kode sumber. Apakah ini masih relevan atau tidak di tahun 2017, saya tidak tahu:

function b64_to_utf8( str ) {
    str = str.replace(/\s/g, '');    
    return decodeURIComponent(escape(window.atob( str )));
}
brandonscript
sumber
1
w3schools.com/jsref/jsref_unescape.asp "Fungsi unescape () tidak digunakan lagi di JavaScript versi 1.5. Gunakan decodeURI () atau decodeURIComponent () sebagai gantinya."
Tedd Hansen
1
Anda menyelamatkan hari-hari saya, bro
Mr Neo
2
Pembaruan: Solusi # 1 di MDN "Masalah Unicode" telah diperbaiki, b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU=');sekarang dengan benar menampilkan "✓ mode à la"
weeix
2
Cara lain untuk memecahkan decodeURIComponent(atob('4pyTIMOgIGxhIG1vZGU=').split('').map(x => '%' + x.charCodeAt(0).toString(16)).join('')) kode bukanlah kode yang paling berkinerja, tetapi itulah yang terjadi.
daniel.gindi
2
return String.fromCharCode(parseInt(p1, 16));agar memiliki kompatibilitas TypeScript.
Martin Schneider
20

Banyak hal berubah. Metode escape / unescape sudah tidak digunakan lagi.

Anda dapat mengenkode URI string sebelum Anda menyandikannya Base64. Perhatikan bahwa ini tidak menghasilkan UTF8 berenkode Base64, melainkan data berenkode URL berenkode Base64. Kedua belah pihak harus menyetujui pengkodean yang sama.

Lihat contoh yang berfungsi di sini: http://codepen.io/anon/pen/PZgbPW

// encode string
var base64 = window.btoa(encodeURIComponent('€ 你好 æøåÆØÅ'));
// decode string
var str = decodeURIComponent(window.atob(tmp));
// str is now === '€ 你好 æøåÆØÅ'

Untuk masalah OP, pustaka pihak ketiga seperti js-base64 harus menyelesaikan masalah.

Tedd Hansen
sumber
1
Saya ingin menunjukkan bahwa Anda tidak memproduksi base64 dari string input, tetapi komponen yang dikodekannya. Jadi jika Anda mengirimkannya, pihak lain tidak dapat mendekodekannya sebagai "base64" dan mendapatkan string asli
Riccardo Galli
3
Anda benar, saya telah memperbarui teks untuk menunjukkannya. Terima kasih. Alternatifnya tampaknya mengimplementasikan base64 sendiri, menggunakan pustaka pihak ketiga (seperti js-base64) atau menerima "Kesalahan: Gagal mengeksekusi 'btoa' di 'Jendela': String yang akan dienkode berisi karakter di luar rentang Latin1. "
Tedd Hansen
14

Jika Anda lebih menyukai string sebagai byte, Anda dapat menggunakan fungsi berikut

function u_atob(ascii) {
    return Uint8Array.from(atob(ascii), c => c.charCodeAt(0));
}

function u_btoa(buffer) {
    var binary = [];
    var bytes = new Uint8Array(buffer);
    for (var i = 0, il = bytes.byteLength; i < il; i++) {
        binary.push(String.fromCharCode(bytes[i]));
    }
    return btoa(binary.join(''));
}


// example, it works also with astral plane characters such as '𝒞'
var encodedString = new TextEncoder().encode('✓');
var base64String = u_btoa(encodedString);
console.log('✓' === new TextDecoder().decode(u_atob(base64String)))
Riccardo Galli
sumber
1
Terima kasih. Jawaban Anda sangat penting dalam membantu saya membuat ini berfungsi, yang membutuhkan waktu berjam-jam selama beberapa hari. +1. stackoverflow.com/a/51814273/470749
Ryan
Untuk solusi lintas-browser yang jauh lebih cepat dan lebih (tetapi pada dasarnya output yang sama), silakan lihat stackoverflow.com/a/53433503/5601591
Jack Giffin
u_atob dan u_btoa menggunakan fungsi yang tersedia di setiap browser sejak IE10 (2012), terlihat kokoh bagi saya (jika Anda merujuk ke TextEncoder, itu hanya sebuah contoh)
Riccardo Galli
5

Berikut adalah solusi yang diperbarui 2018 seperti yang dijelaskan dalam Sumber Daya Pengembangan Mozilla

UNTUK ENKODE DARI UNICODE KE B64

function b64EncodeUnicode(str) {
    // first we use encodeURIComponent to get percent-encoded UTF-8,
    // then we convert the percent encodings into raw bytes which
    // can be fed into btoa.
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
        function toSolidBytes(match, p1) {
            return String.fromCharCode('0x' + p1);
    }));
}

b64EncodeUnicode('✓ à la mode'); // "4pyTIMOgIGxhIG1vZGU="
b64EncodeUnicode('\n'); // "Cg=="

UNTUK DEKODE DARI B64 KE UNICODE

function b64DecodeUnicode(str) {
    // Going backwards: from bytestream, to percent-encoding, to original string.
    return decodeURIComponent(atob(str).split('').map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}

b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"
b64DecodeUnicode('Cg=='); // "\n"
Manuel G
sumber
4

Artikel lengkap yang cocok untuk saya: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding

Bagian tempat kami menyandikan dari Unicode / UTF-8 adalah

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"

Ini adalah salah satu metode yang paling banyak digunakan saat ini.

rika
sumber
Itu tautan yang sama dengan jawaban yang diterima.
brandonscript
3

Saya akan berasumsi bahwa seseorang mungkin menginginkan solusi yang menghasilkan URI base64 yang dapat digunakan secara luas. Silahkan kunjungi data:text/plain;charset=utf-8;base64,4pi44pi54pi64pi74pi84pi+4pi/untuk melihat demo (copy data uri, buka tab baru, paste data URI ke address bar, lalu tekan enter untuk menuju halaman). Terlepas dari kenyataan bahwa URI ini dikodekan dengan base64, browser masih dapat mengenali titik kode tinggi dan mendekodekannya dengan benar. Encoder + decoder yang diperkecil adalah 1058 byte (+ Gzip → 589 byte)

!function(e){"use strict";function h(b){var a=b.charCodeAt(0);if(55296<=a&&56319>=a)if(b=b.charCodeAt(1),b===b&&56320<=b&&57343>=b){if(a=1024*(a-55296)+b-56320+65536,65535<a)return d(240|a>>>18,128|a>>>12&63,128|a>>>6&63,128|a&63)}else return d(239,191,189);return 127>=a?inputString:2047>=a?d(192|a>>>6,128|a&63):d(224|a>>>12,128|a>>>6&63,128|a&63)}function k(b){var a=b.charCodeAt(0)<<24,f=l(~a),c=0,e=b.length,g="";if(5>f&&e>=f){a=a<<f>>>24+f;for(c=1;c<f;++c)a=a<<6|b.charCodeAt(c)&63;65535>=a?g+=d(a):1114111>=a?(a-=65536,g+=d((a>>10)+55296,(a&1023)+56320)):c=0}for(;c<e;++c)g+="\ufffd";return g}var m=Math.log,n=Math.LN2,l=Math.clz32||function(b){return 31-m(b>>>0)/n|0},d=String.fromCharCode,p=atob,q=btoa;e.btoaUTF8=function(b,a){return q((a?"\u00ef\u00bb\u00bf":"")+b.replace(/[\x80-\uD7ff\uDC00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g,h))};e.atobUTF8=function(b,a){a||"\u00ef\u00bb\u00bf"!==b.substring(0,3)||(b=b.substring(3));return p(b).replace(/[\xc0-\xff][\x80-\xbf]*/g,k)}}(""+void 0==typeof global?""+void 0==typeof self?this:self:global)

Di bawah ini adalah kode sumber yang digunakan untuk membuatnya.

var fromCharCode = String.fromCharCode;
var btoaUTF8 = (function(btoa, replacer){"use strict";
    return function(inputString, BOMit){
        return btoa((BOMit ? "\xEF\xBB\xBF" : "") + inputString.replace(
            /[\x80-\uD7ff\uDC00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g, replacer
        ));
    }
})(btoa, function(nonAsciiChars){"use strict";
    // make the UTF string into a binary UTF-8 encoded string
    var point = nonAsciiChars.charCodeAt(0);
    if (point >= 0xD800 && point <= 0xDBFF) {
        var nextcode = nonAsciiChars.charCodeAt(1);
        if (nextcode !== nextcode) // NaN because string is 1 code point long
            return fromCharCode(0xef/*11101111*/, 0xbf/*10111111*/, 0xbd/*10111101*/);
        // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
        if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) {
            point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000;
            if (point > 0xffff)
                return fromCharCode(
                    (0x1e/*0b11110*/<<3) | (point>>>18),
                    (0x2/*0b10*/<<6) | ((point>>>12)&0x3f/*0b00111111*/),
                    (0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/),
                    (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/)
                );
        } else return fromCharCode(0xef, 0xbf, 0xbd);
    }
    if (point <= 0x007f) return nonAsciiChars;
    else if (point <= 0x07ff) {
        return fromCharCode((0x6<<5)|(point>>>6), (0x2<<6)|(point&0x3f));
    } else return fromCharCode(
        (0xe/*0b1110*/<<4) | (point>>>12),
        (0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/),
        (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/)
    );
});

Kemudian, untuk mendekode data base64, HTTP dapatkan data tersebut sebagai URI data atau gunakan fungsi di bawah ini.

var clz32 = Math.clz32 || (function(log, LN2){"use strict";
    return function(x) {return 31 - log(x >>> 0) / LN2 | 0};
})(Math.log, Math.LN2);
var fromCharCode = String.fromCharCode;
var atobUTF8 = (function(atob, replacer){"use strict";
    return function(inputString, keepBOM){
        inputString = atob(inputString);
        if (!keepBOM && inputString.substring(0,3) === "\xEF\xBB\xBF")
            inputString = inputString.substring(3); // eradicate UTF-8 BOM
        // 0xc0 => 0b11000000; 0xff => 0b11111111; 0xc0-0xff => 0b11xxxxxx
        // 0x80 => 0b10000000; 0xbf => 0b10111111; 0x80-0xbf => 0b10xxxxxx
        return inputString.replace(/[\xc0-\xff][\x80-\xbf]*/g, replacer);
    }
})(atob, function(encoded){"use strict";
    var codePoint = encoded.charCodeAt(0) << 24;
    var leadingOnes = clz32(~codePoint);
    var endPos = 0, stringLen = encoded.length;
    var result = "";
    if (leadingOnes < 5 && stringLen >= leadingOnes) {
        codePoint = (codePoint<<leadingOnes)>>>(24+leadingOnes);
        for (endPos = 1; endPos < leadingOnes; ++endPos)
            codePoint = (codePoint<<6) | (encoded.charCodeAt(endPos)&0x3f/*0b00111111*/);
        if (codePoint <= 0xFFFF) { // BMP code point
          result += fromCharCode(codePoint);
        } else if (codePoint <= 0x10FFFF) {
          // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
          codePoint -= 0x10000;
          result += fromCharCode(
            (codePoint >> 10) + 0xD800,  // highSurrogate
            (codePoint & 0x3ff) + 0xDC00 // lowSurrogate
          );
        } else endPos = 0; // to fill it in with INVALIDs
    }
    for (; endPos < stringLen; ++endPos) result += "\ufffd"; // replacement character
    return result;
});

Keuntungan menjadi lebih standar adalah encoder dan decoder ini lebih dapat diterapkan secara luas karena dapat digunakan sebagai URL valid yang ditampilkan dengan benar. Mengamati.

(function(window){
    "use strict";
    var sourceEle = document.getElementById("source");
    var urlBarEle = document.getElementById("urlBar");
    var mainFrameEle = document.getElementById("mainframe");
    var gotoButton = document.getElementById("gotoButton");
    var parseInt = window.parseInt;
    var fromCodePoint = String.fromCodePoint;
    var parse = JSON.parse;
    
    function unescape(str){
        return str.replace(/\\u[\da-f]{0,4}|\\x[\da-f]{0,2}|\\u{[^}]*}|\\[bfnrtv"'\\]|\\0[0-7]{1,3}|\\\d{1,3}/g, function(match){
          try{
            if (match.startsWith("\\u{"))
              return fromCodePoint(parseInt(match.slice(2,-1),16));
            if (match.startsWith("\\u") || match.startsWith("\\x"))
              return fromCodePoint(parseInt(match.substring(2),16));
            if (match.startsWith("\\0") && match.length > 2)
              return fromCodePoint(parseInt(match.substring(2),8));
            if (/^\\\d/.test(match)) return fromCodePoint(+match.slice(1));
          }catch(e){return "\ufffd".repeat(match.length)}
          return parse('"' + match + '"');
        });
    }
    
    function whenChange(){
      try{ urlBarEle.value = "data:text/plain;charset=UTF-8;base64," + btoaUTF8(unescape(sourceEle.value), true);
      } finally{ gotoURL(); }
    }
    sourceEle.addEventListener("change",whenChange,{passive:1});
    sourceEle.addEventListener("input",whenChange,{passive:1});
    
    // IFrame Setup:
    function gotoURL(){mainFrameEle.src = urlBarEle.value}
    gotoButton.addEventListener("click", gotoURL, {passive: 1});
    function urlChanged(){urlBarEle.value = mainFrameEle.src}
    mainFrameEle.addEventListener("load", urlChanged, {passive: 1});
    urlBarEle.addEventListener("keypress", function(evt){
      if (evt.key === "enter") evt.preventDefault(), urlChanged();
    }, {passive: 1});
    
        
    var fromCharCode = String.fromCharCode;
    var btoaUTF8 = (function(btoa, replacer){
		    "use strict";
        return function(inputString, BOMit){
        	return btoa((BOMit?"\xEF\xBB\xBF":"") + inputString.replace(
        		/[\x80-\uD7ff\uDC00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g, replacer
    		));
    	}
    })(btoa, function(nonAsciiChars){
		"use strict";
    	// make the UTF string into a binary UTF-8 encoded string
    	var point = nonAsciiChars.charCodeAt(0);
    	if (point >= 0xD800 && point <= 0xDBFF) {
    		var nextcode = nonAsciiChars.charCodeAt(1);
    		if (nextcode !== nextcode) { // NaN because string is 1code point long
    			return fromCharCode(0xef/*11101111*/, 0xbf/*10111111*/, 0xbd/*10111101*/);
    		}
    		// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
    		if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) {
    			point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000;
    			if (point > 0xffff) {
    				return fromCharCode(
    					(0x1e/*0b11110*/<<3) | (point>>>18),
    					(0x2/*0b10*/<<6) | ((point>>>12)&0x3f/*0b00111111*/),
    					(0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/),
    					(0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/)
    				);
    			}
    		} else {
    			return fromCharCode(0xef, 0xbf, 0xbd);
    		}
    	}
    	if (point <= 0x007f) { return inputString; }
    	else if (point <= 0x07ff) {
    		return fromCharCode((0x6<<5)|(point>>>6), (0x2<<6)|(point&0x3f/*00111111*/));
    	} else {
    		return fromCharCode(
    			(0xe/*0b1110*/<<4) | (point>>>12),
    			(0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/),
    			(0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/)
    		);
    	}
    });
    setTimeout(whenChange, 0);
})(window);
img:active{opacity:0.8}
<center>
<textarea id="source" style="width:66.7vw">Hello \u1234 W\186\0256ld!
Enter text into the top box. Then the URL will update automatically.
</textarea><br />
<div style="width:66.7vw;display:inline-block;height:calc(25vw + 1em + 6px);border:2px solid;text-align:left;line-height:1em">
<input id="urlBar" style="width:calc(100% - 1em - 13px)" /><img id="gotoButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAeCAMAAADqx5XUAAAAclBMVEX///9NczZ8e32ko6fDxsU/fBoSQgdFtwA5pAHVxt+7vLzq5ex23y4SXABLiiTm0+/c2N6DhoQ6WSxSyweVlZVvdG/Uz9aF5kYlbwElkwAggACxs7Jl3hX07/cQbQCar5SU9lRntEWGum+C9zIDHwCGnH5IvZAOAAABmUlEQVQoz7WS25acIBBFkRLkIgKKtOCttbv//xdDmTGZzHv2S63ltuBQQP4rdRiRUP8UK4wh6nVddQwj/NtDQTvac8577zTQb72zj65/876qqt7wykU6/1U6vFEgjE1mt/5LRqrpu7oVsn0sjZejMfxR3W/yLikqAFcUx93YxLmZGOtElmEu6Ufd9xV3ZDTGcEvGLbMk0mHHlUSvS5svCwS+hVL8loQQyfpI1Ay8RF/xlNxcsTchGjGDIuBG3Ik7TMyNxn8m0TSnBAK6Z8UZfp3IbAonmJvmsEACum6aNv7B0CnvpezDcNhw9XWsuAr7qnRg6dABmeM4dTgn/DZdXWs3LMspZ1KDMt1kcPJ6S1icWNp2qaEmjq6myx7jbQK3VKItLJaW5FR+cuYlRhYNKzGa9vF4vM5roLW3OSVjkmiGJrPhUq301/16pVKZRGFYWjTP50spTxBN5Z4EKnSonruk+n4tUokv1aJSEl/MLZU90S3L6/U6o0J142iQVp3HcZxKSo8LfkNRCtJaKYFSRX7iaoAAUDty8wvWYR6HJEepdwAAAABJRU5ErkJggg==" style="width:calc(1em + 4px);line-height:1em;vertical-align:-40%;cursor:pointer" />
<iframe id="mainframe" style="width:66.7vw;height:25vw" frameBorder="0"></iframe>
</div>
</center>

Selain sangat terstandarisasi, potongan kode di atas juga sangat cepat. Alih-alih rantai suksesi tidak langsung di mana data harus diubah beberapa kali antara berbagai bentuk (seperti dalam tanggapan Riccardo Galli), potongan kode di atas adalah sesingkat mungkin. Ini hanya menggunakan satu String.prototype.replacepanggilan cepat sederhana untuk memproses data saat encoding, dan hanya satu untuk mendekode data saat mendekode. Kelebihan lainnya adalah (terutama untuk string besar)String.prototype.replace memungkinkan browser untuk secara otomatis menangani manajemen memori yang mendasari mengubah ukuran string, memimpin peningkatan kinerja yang signifikan terutama di browser yang selalu hijau seperti Chrome dan Firefox yang sangat dioptimalkanString.prototype.replace. Terakhir, lapisan gula pada kue adalah untuk Anda pengguna skrip latin exclūsīvō, string yang tidak berisi poin kode di atas 0x7f akan diproses lebih cepat karena string tetap tidak dimodifikasi oleh algoritme pengganti.

Saya telah membuat repositori github untuk solusi ini di https://github.com/anonyco/BestBase64EncoderDecoder/

Jack Giffin
sumber
Dapatkah Anda menjelaskan apa yang Anda maksud dengan "cara yang dibuat oleh pengguna" vs. "dapat diinterpretasikan oleh browser"? Apa nilai tambah dari penggunaan solusi ini, katakanlah, apa yang direkomendasikan Mozilla?
brandonscript
@brandonscript Mozilla berbeda dari MDN. MDN adalah konten yang dibuat pengguna. Halaman di MDN yang merekomendasikan solusi Anda adalah konten yang dibuat pengguna, bukan konten yang dibuat oleh vendor browser.
Jack Giffin
Apakah vendor solusi Anda dibuat? Saya akan begitu, saya sarankan memberikan kredit ke asal. Jika tidak, maka itu juga dibuat oleh pengguna, dan tidak berbeda dengan jawaban MDN?
brandonscript
@brandcript Poin bagus. Anda benar. Saya menghapus bagian teks itu. Juga, lihat demo yang saya tambahkan.
Jack Giffin
0

Koreksi kecil, unescape dan escape tidak digunakan lagi, jadi:

function utf8_to_b64( str ) {
    return window.btoa(decodeURIComponent(encodeURIComponent(str)));
}

function b64_to_utf8( str ) {
     return decodeURIComponent(encodeURIComponent(window.atob(str)));
}


function b64_to_utf8( str ) {
    str = str.replace(/\s/g, '');    
    return decodeURIComponent(encodeURIComponent(window.atob(str)));
}
Darkves
sumber
2
Sepertinya tautan dokumen berbeda dari sekarang, menyarankan solusi regex untuk mengelolanya.
brandonscript
2
Ini tidak akan berhasil, karena encodeURIComponentmerupakan kebalikan dari decodeURIComponent, yaitu hanya akan membatalkan konversi. Lihat stackoverflow.com/a/31412163/1534459 untuk penjelasan yang bagus tentang apa yang terjadi dengan escapedan unescape.
bodo
1
@canaaerus Saya tidak mengerti komentar Anda? escape dan unescape tidak digunakan lagi, saya hanya menukarnya dengan [decode | encode] fungsi URIComponent :-) Semuanya bekerja dengan baik. Baca pertanyaannya terlebih dahulu
Darkves
1
@Darkves: Alasan mengapa encodeURIComponentdigunakan, adalah untuk menangani (seluruh rentang) string unicode dengan benar. Jadi misal window.btoa(decodeURIComponent(encodeURIComponent('€')))memberi Error: String contains an invalid characterkarena sama dengan window.btoa('€')dan btoatidak bisa menyandikan .
bodo
2
@Darkves: Ya, itu benar. Tetapi Anda tidak dapat menukar escape dengan EncodeURIComponent dan unescape dengan DecodeURIComponent, karena Encode dan metode escape tidak melakukan hal yang sama. Sama dengan decode & unescape. Saya awalnya melakukan kesalahan yang sama, btw. Anda harus memperhatikan bahwa jika Anda mengambil string, UriEncode itu, lalu UriDecode itu, Anda mendapatkan kembali string yang sama dengan yang Anda masukkan. Jadi melakukan itu akan menjadi omong kosong. Saat Anda melepas string yang dikodekan dengan encodeURIComponent, Anda tidak mendapatkan string yang sama dengan yang Anda masukkan, jadi itulah mengapa dengan escape / unescape berfungsi, tetapi tidak dengan milik Anda.
Stefan Steiger
0

Berikut beberapa kode bukti masa depan untuk browser yang mungkin kurang escape/unescape(). Perhatikan bahwa IE 9 dan yang lebih lama tidak mendukung atob/btoa(), jadi Anda perlu menggunakan fungsi base64 khusus untuk mereka.

// Polyfill for escape/unescape
if( !window.unescape ){
    window.unescape = function( s ){
        return s.replace( /%([0-9A-F]{2})/g, function( m, p ) {
            return String.fromCharCode( '0x' + p );
        } );
    };
}
if( !window.escape ){
    window.escape = function( s ){
        var chr, hex, i = 0, l = s.length, out = '';
        for( ; i < l; i ++ ){
            chr = s.charAt( i );
            if( chr.search( /[A-Za-z0-9\@\*\_\+\-\.\/]/ ) > -1 ){
                out += chr; continue; }
            hex = s.charCodeAt( i ).toString( 16 );
            out += '%' + ( hex.length % 2 != 0 ? '0' : '' ) + hex;
        }
        return out;
    };
}

// Base64 encoding of UTF-8 strings
var utf8ToB64 = function( s ){
    return btoa( unescape( encodeURIComponent( s ) ) );
};
var b64ToUtf8 = function( s ){
    return decodeURIComponent( escape( atob( s ) ) );
};

Contoh yang lebih komprehensif untuk encoding dan decoding UTF-8 dapat ditemukan di sini: http://jsfiddle.net/47zwb41o/

Beejor
sumber
-1

termasuk solusi di atas jika masih menghadapi masalah coba seperti di bawah ini, Pertimbangkan kasus di mana escape tidak didukung untuk TS.

blob = new Blob(["\ufeff", csv_content]); // this will make symbols to appears in excel 

untuk csv_content Anda bisa mencoba seperti di bawah ini.

function b64DecodeUnicode(str: any) {        
        return decodeURIComponent(atob(str).split('').map((c: any) => {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));
    }
Diwakar
sumber