L'utilizzo dell'atob di Javascript per decodificare base64 non decodifica correttamente le stringhe utf-8


106

Sto usando la window.atob()funzione Javascript per decodificare una stringa con codifica base64 (in particolare il contenuto con codifica base64 dall'API GitHub). Il problema è che sto recuperando i caratteri con codifica ASCII (come â¢invece di ). Come posso gestire correttamente il flusso in entrata con codifica base64 in modo che sia decodificato come utf-8?


3
La pagina MDN che hai collegato ha un paragrafo che inizia con la frase "Da utilizzare con stringhe Unicode o UTF-8".
Pointy

1
Sei su node? Ci sono soluzioni migliori diatob
Bergi

Risposte:


269

C'è un ottimo articolo sui documenti MDN di Mozilla che descrive esattamente questo problema:

Il "problema Unicode" Poiché le DOMStrings sono stringhe codificate a 16 bit, nella maggior parte dei browser che chiamano window.btoauna stringa Unicode causerà un Character Out Of Range exceptionse un carattere supera l'intervallo di un byte a 8 bit (0x00 ~ 0xFF). Esistono due metodi possibili per risolvere questo problema:

  • il primo è fare l'escape dell'intera stringa (con UTF-8, vedi encodeURIComponent) e poi codificarla;
  • il secondo è convertire l'UTF-16 DOMStringin un array di caratteri UTF-8 e quindi codificarlo.

Una nota sulle soluzioni precedenti: l'articolo MDN originariamente suggeriva di utilizzare unescapee escapeper risolvere il Character Out Of Rangeproblema delle eccezioni, ma da allora sono state deprecate. Alcune altre risposte qui hanno suggerito di aggirare questo problema con decodeURIComponente encodeURIComponent, questo si è dimostrato inaffidabile e imprevedibile. L'aggiornamento più recente a questa risposta utilizza le moderne funzioni JavaScript per migliorare la velocità e modernizzare il codice.

Se stai cercando di risparmiare tempo, potresti anche considerare l'utilizzo di una libreria:

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

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

La soluzione pre-2018 (funzionale, e anche se probabilmente un supporto migliore per i browser meno recenti, non aggiornata)

Ecco la raccomandazione corrente, diretta da MDN, con qualche compatibilità aggiuntiva con TypeScript tramite @ 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"

La soluzione originale (obsoleta)

Questo usato escapee unescape(che ora sono deprecati, sebbene funzioni ancora in tutti i browser moderni):

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"

E un'ultima cosa: ho riscontrato questo problema per la prima volta quando ho chiamato l'API GitHub. Per farlo funzionare correttamente su Safari (mobile), in realtà ho dovuto rimuovere tutto lo spazio bianco dalla fonte base64 prima di poter persino decodificare la fonte. Se questo è ancora rilevante nel 2017, non lo so:

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

1
w3schools.com/jsref/jsref_unescape.asp "La funzione unescape () è stata deprecata in JavaScript versione 1.5. Utilizzare invece decodeURI () o decodeURIComponent ()."
Tedd Hansen

1
Mi hai salvato i giorni, fratello
Mr Neo

2
Aggiornamento: la soluzione n. 1 nel "Problema Unicode" di MDN è stata risolta, b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU=');ora viene visualizzato correttamente "✓ alla modalità"
weeix

2
Un altro modo per decodificare sarebbe decodeURIComponent(atob('4pyTIMOgIGxhIG1vZGU=').split('').map(x => '%' + x.charCodeAt(0).toString(16)).join('')) Non il codice più performante, ma è quello che è.
daniel.gindi

2
return String.fromCharCode(parseInt(p1, 16));per avere la compatibilità TypeScript.
Martin Schneider

20

Le cose cambiano. I metodi escape / unescape sono stati deprecati.

Puoi URI codificare la stringa prima di codificarla in Base64. Si noti che questo non produce dati UTF8 con codifica Base64, ma piuttosto dati codificati URL codificati Base64. Entrambe le parti devono concordare sulla stessa codifica.

Vedi un esempio funzionante qui: http://codepen.io/anon/pen/PZgbPW

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

Per il problema di OP una libreria di terze parti come js-base64 dovrebbe risolvere il problema.


1
Vorrei sottolineare che non stai producendo la base64 della stringa di input, ma del suo componente codificato. Quindi se lo mandi via l'altra parte non può decodificarlo come "base64" e ottenere la stringa originale
Riccardo Galli

3
Hai ragione, ho aggiornato il testo per farlo notare. Grazie. L'alternativa sembra implementare base64 da soli, utilizzando una libreria di terze parti (come js-base64) o ricevere "Errore: impossibile eseguire" btoa "su" Finestra ": la stringa da codificare contiene caratteri al di fuori dell'intervallo Latin1. "
Tedd Hansen

14

Se trattare le stringhe come byte è più la tua passione, puoi usare le seguenti funzioni

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

1
Grazie. La tua risposta è stata fondamentale per aiutarmi a ottenere questo risultato, che mi ha richiesto molte ore in più giorni. +1. stackoverflow.com/a/51814273/470749
Ryan

Per una soluzione molto più veloce e cross-browser (ma essenzialmente lo stesso output), vedere stackoverflow.com/a/53433503/5601591
Jack Giffin

u_atob e u_btoa utilizzano funzioni disponibili in tutti i browser da IE10 (2012), mi sembra solido (se ti riferisci a TextEncoder, questo è solo un esempio)
Riccardo Galli

5

Ecco la soluzione aggiornata al 2018 come descritto nelle risorse di sviluppo di Mozilla

PER CODIFICARE DA UNICODE A 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=="

PER DECODIFICARE DA B64 A 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"

4

L'articolo completo che funziona per me: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding

La parte in cui codifichiamo da Unicode / UTF-8 è

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"

Questo è uno dei metodi più utilizzati al giorno d'oggi.


Questo è lo stesso collegamento della risposta accettata.
brandonscript,

3

Presumo che si possa desiderare una soluzione che produca un URI base64 ampiamente utilizzabile. Visita data:text/plain;charset=utf-8;base64,4pi44pi54pi64pi74pi84pi+4pi/per vedere una dimostrazione (copia l'URI dei dati, apri una nuova scheda, incolla l'URI dei dati nella barra degli indirizzi, quindi premi Invio per andare alla pagina). Nonostante il fatto che questo URI sia codificato in base64, il browser è ancora in grado di riconoscere i punti di codice alti e decodificarli correttamente. L'encoder minificato + decoder è 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 seguito è riportato il codice sorgente utilizzato per generarlo.

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

Quindi, per decodificare i dati base64, HTTP ottiene i dati come URI di dati o utilizza la funzione seguente.

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

Il vantaggio di essere più standard è che questo codificatore e questo decodificatore sono più ampiamente applicabili perché possono essere utilizzati come un URL valido che viene visualizzato correttamente. Osservare.

(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>

Oltre ad essere molto standardizzati, gli snippet di codice sopra sono anche molto veloci. Invece di una catena di successione indiretta in cui i dati devono essere convertiti più volte tra varie forme (come nella risposta di Riccardo Galli), lo snippet di codice sopra è il più diretto possibile. Usa solo un semplice veloce . Infine, la ciliegina sulla torta è che per voi utenti di script latino exclūsīvō, le stringhe che non contengono alcun punto di codice sopra 0x7f sono molto veloci da elaborare perché la stringa rimane non modificata dall'algoritmo di sostituzione.String.prototype.replace chiamata per elaborare i dati durante la codifica e solo una per decodificare i dati durante la decodifica. Un altro vantaggio è che (soprattutto per le stringhe di grandi dimensioni), String.prototype.replaceconsente al browser di gestire automaticamente la gestione della memoria sottostante per il ridimensionamento della stringa, portando un significativo aumento delle prestazioni soprattutto nei browser evergreen come Chrome e Firefox che ottimizzano pesantementeString.prototype.replace

Ho creato un repository github per questa soluzione su https://github.com/anonyco/BestBase64EncoderDecoder/


Puoi approfondire cosa intendi per "modo creato dall'utente" e "interpretabile dal browser"? Qual è il valore aggiunto dell'utilizzo di questa soluzione rispetto, ad esempio, a ciò che Mozilla consiglia?
brandonscript

@brandonscript Mozilla è diverso da MDN. MDN è contenuto creato dall'utente. La pagina su MDN che consiglia la tua soluzione era contenuto creato dall'utente, non contenuto creato dal fornitore del browser.
Jack Giffin

Il tuo fornitore di soluzioni è stato creato? Quindi, suggerirei di dare credito all'origine. In caso contrario, è anche creato dall'utente e non è diverso dalla risposta di MDN?
brandonscript

@brandonscript Buon punto. Hai ragione. Ho rimosso quel pezzo di testo. Inoltre, controlla la demo che ho aggiunto.
Jack Giffin

0

Piccola correzione, annullamento dell'escape e fuga sono deprecati, quindi:

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

2
Sembra che il collegamento al documento sia anche diverso da questo ora, suggerendo una soluzione regex per gestirlo.
brandonscript

2
Questo non funzionerà, perché encodeURIComponentè l'inverso di decodeURIComponent, cioè annullerà semplicemente la conversione. Vedi stackoverflow.com/a/31412163/1534459 per un'ottima spiegazione di ciò che sta accadendo con escapee unescape.
bodo

1
@canaaerus non capisco il tuo commento? escape e unescape sono deprecati, li sostituisco con la funzione [decode | encode] URIComponent :-) Tutto funziona perfettamente. Leggi prima la domanda
Darkves

1
@ Darkves: il motivo per cui encodeURIComponentviene utilizzato è quello di gestire correttamente (l'intera gamma di) stringhe Unicode. Quindi, ad esempio, window.btoa(decodeURIComponent(encodeURIComponent('€')))Error: String contains an invalid characterperché è uguale a window.btoa('€')e btoanon può codificare .
bodo

2
@ Darkves: Sì, è corretto. Ma non puoi scambiare escape con EncodeURIComponent e unescape con DecodeURIComponent, perché i metodi Encode e escape non fanno la stessa cosa. Lo stesso con decode e unescape. Inizialmente ho fatto lo stesso errore, btw. Dovresti notare che se prendi una stringa, UriEncode, quindi UriDecode, ottieni la stessa stringa che hai inserito. Quindi farlo sarebbe una sciocchezza. Quando annulli l'escape di una stringa codificata con encodeURIComponent, non ottieni la stessa stringa che hai immesso, quindi è per questo che con escape / unescape funziona, ma non con il tuo.
Stefan Steiger

0

Ecco un po 'di codice a prova di futuro per i browser che potrebbero mancare escape/unescape(). Nota che IE 9 e versioni precedenti non supportano atob/btoa(), quindi dovresti utilizzare le funzioni base64 personalizzate per loro.

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

Un esempio più completo per la codifica e decodifica UTF-8 può essere trovato qui: http://jsfiddle.net/47zwb41o/


-1

includendo la soluzione precedente se il problema persiste, prova come di seguito, Considera il caso in cui la fuga non è supportata per TS.

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

per csv_content puoi provare come di seguito.

function b64DecodeUnicode(str: any) {        
        return decodeURIComponent(atob(str).split('').map((c: any) => {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.