traductor online imagen decodificar codificacion btoa javascript encoding utf-8

online - decodificar imagen base64 javascript



El uso de atob de Javascript para decodificar base64 no decodifica correctamente las cadenas utf-8 (8)

Estoy usando la función Javascript window.atob() para decodificar una cadena codificada en base64 (específicamente el contenido codificado en base64 de la API de GitHub). El problema es que estoy recuperando los caracteres codificados en ASCII (como ⢠lugar de ). ¿Cómo puedo manejar adecuadamente la transmisión entrante codificada en base64 para que se decodifique como utf-8?


Aquí está la solución actualizada de 2018 como se describe en los Recursos de desarrollo de Mozilla

PARA CODIFICAR DESDE 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=="

PARA DECODIFICAR DE 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"


Aquí hay un código a prueba de futuro para los navegadores que pueden carecer de escape/unescape() . Tenga en cuenta que IE 9 y atob/btoa() anteriores no son compatibles con atob/btoa() , por lo que deberá utilizar funciones de base64 personalizadas para ellos.

// 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 ejemplo más completo para la codificación y decodificación UTF-8 se puede encontrar aquí: http://jsfiddle.net/47zwb41o/


Hay un gran artículo sobre los documentos MDN de Mozilla que describe exactamente este problema:

El "problema de Unicode" Dado que DOMString s son cadenas codificadas de 16 bits, en la mayoría de los navegadores que llaman a window.btoa en una cadena Unicode provocará una Character Out Of Range exception si un carácter excede el rango de un byte de 8 bits (0x00 ~ 0xFF). Hay dos métodos posibles para resolver este problema:

  • el primero es escapar de toda la cadena (con UTF-8, ver encodeURIComponent ) y luego codificarlo;
  • el segundo es convertir el DOMString UTF-16 en una matriz de caracteres UTF-8 y luego codificarlo.

Una nota sobre soluciones anteriores: el artículo de MDN sugirió originalmente usar unescape y escape para resolver el problema de excepción de Character Out Of Range , pero desde entonces han quedado en desuso. Algunas otras respuestas aquí han sugerido trabajar alrededor de esto con decodeURIComponent y encodeURIComponent , esto ha demostrado ser poco confiable e impredecible. La actualización más reciente de esta respuesta utiliza funciones modernas de JavaScript para mejorar la velocidad y modernizar el código.

Si está tratando de ahorrar algo de tiempo, también podría considerar usar una biblioteca:

Codificación 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=="

Decodificación 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 solución anterior a 2018 (funcional, y aunque probablemente mejor soporte para navegadores antiguos, no actualizada)

Aquí está la recomendación actual, directa de MDN, con alguna compatibilidad adicional con TypeScript a través de @ 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 solución original (en desuso)

Esto usó escape y unescape (que ahora están en desuso, aunque todavía funciona en todos los navegadores modernos):

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"

Y una última cosa: encontré este problema por primera vez al llamar a la API de GitHub. Para que esto funcione correctamente en Safari (móvil), tuve que quitar todo el espacio en blanco de la fuente base64 antes de poder decodificar la fuente. Si esto sigue siendo relevante o no en 2017, no sé:

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


Las cosas cambian. Los métodos de w3schools.com/jsref/jsref_unescape.asp han quedado en desuso.

Puede URI codificar la cadena antes de codificarla en Base64. Tenga en cuenta que esto no produce UTF8 codificado en Base64, sino datos codificados en URL codificados en Base64. Ambas partes deben acordar la misma codificación.

Vea el ejemplo de trabajo aquí: http://codepen.io/anon/pen/PZgbPW

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

Para el problema de OP, una biblioteca de terceros como js-base64 debería resolver el problema.


Pequeñas correcciones, unescape y escape están en desuso, así que:

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


Si lo tuyo es tratar las cadenas como bytes, puedes usar las siguientes funciones

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


Supongo que uno podría querer una solución que produzca un URI base64 ampliamente utilizable. Visite los data:text/plain;charset=utf-8;base64,4pi44pi54pi64pi74pi84pi+4pi/ para ver una demostración (copie el uri de datos, abra una nueva pestaña, pegue el URI de datos en la barra de direcciones, luego presione Intro para ir a la página). A pesar de que este URI está codificado en base64, el navegador aún puede reconocer los puntos de código altos y decodificarlos correctamente. El codificador + decodificador minificado es de 1058 bytes (+ Gzip → 589 bytes)

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

A continuación se muestra el código fuente utilizado para 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*/) ); });

Luego, para decodificar los datos de base64, HTTP obtiene los datos como un URI de datos o usa la función a continuación.

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

La ventaja de ser más estándar es que este codificador y este decodificador son más ampliamente aplicables porque pueden usarse como una URL válida que se muestra correctamente. Observar.

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

Además de estar muy estandarizado, los fragmentos de código anteriores también son muy rápidos. En lugar de una cadena de sucesión indirecta donde los datos deben convertirse varias veces entre varias formas (como en la respuesta de Riccardo Galli), el fragmento de código anterior es lo más directo posible. Utiliza solo una simple llamada rápida String.prototype.replace para procesar los datos al codificar, y solo una para decodificar los datos al decodificar. Otra ventaja es que (especialmente para cadenas grandes), String.prototype.replace permite que el navegador maneje automáticamente la administración de memoria subyacente para cambiar el tamaño de la cadena, lo que lleva a un aumento significativo del rendimiento, especialmente en navegadores de hoja perenne como Chrome y Firefox que optimizan en gran medida el String.prototype.replace . Finalmente, la guinda del pastel es que para los usuarios de exclūsīvō de escritura latina, las cadenas que no contienen ningún punto de código superior a 0x7f son más rápidas de procesar porque la cadena permanece sin modificaciones por el algoritmo de reemplazo.

He creado un repositorio de github para esta solución en https://github.com/anonyco/BestBase64EncoderDecoder/


incluida la solución anterior si aún tiene problemas, intente como a continuación, considere el caso en el que escape no es compatible con TS.

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

para csv_content puedes probar como a continuación.

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