使用JavaScript的atob来解码base64不能正确解码utf-8string

我正在使用Javascript window.atob()函数来解码base64编码的string(特别是来自GitHub API的base64编码的内容)。 问题是我得到了ASCII编码的字符(像?而不是 )。 我怎样才能正确处理传入的base64编码stream,以便解码为utf-8?

Mozilla MDN上有一篇很棒的文章 ,描述了这个问题:

“Unicode问题”由于DOMStrings是16位编码的string,因此在大多数浏览器中调用一个Unicodestring的window.btoa将导致字符超出范围exception,如果一个字符超出了8位ASCII编码字符的范围。 有两种可能的方法来解决这个问题:

  • 第一个是逃避整个string,然后编码;
  • 第二个是将UTF-16 DOMString转换为UTF-8字符数组,然后对其进行编码。

关于原始答案的说明:以前,MDN文章build议使用unescapeescape来解决Character Out Of Rangeexception问题,但是此后不再使用。 这里有一些其他的答案build议解决这个与decodeURIComponentencodeURIComponent ,这已被certificate是不可靠和不可预知的。

最后,你可以通过使用图书馆来保存一些悲伤:

  • js-base64 (NPM,非常适合Node.js)
  • 基于64位的js

这里是目前的build议,直接从MDN,通过@ MA-Maddin的一些额外的TypeScript兼容性:

编码UTF8→base64

使用正则expression式来代替已弃用的unescape函数

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

解码base64⇢UTF8

MDN文章本来没有解码的例子,但现在已经增加了一个

 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" 

原来的解决scheme(已弃用)

这个使用了escapeunescape (现在不推荐使用,尽pipe这个方法在所有现代浏览器中仍然有效):

 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" 

还有最后一件事:我在调用GitHub API时第一次遇到这个问题。 为了正确使用(Mobile)Safari,我实际上必须从base64源文件中清除所有空白区域, 然后才能对源文件进行解码。 不pipe这个在2017年还是相关的,我不知道:

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

事情会改变的。 escape / unescape方法已被弃用。

在对Base64进行编码之前,您可以对该string进行URI编码。 请注意,这不会生成Base64编码的UTF8,而是生成Base64编码的URL编码数据。 双方必须就相同的编码达成一致。

看到这里的工作示例: http : //codepen.io/anon/pen/PZgbPW

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

对于OP的问题,像js-base64这样的第三方库应该解决这个问题。

小的更正,unescape和escape都被弃用了,所以:

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

如果将string视为字节更重要,则可以使用以下函数

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

以下是一些可能缺lessescape/unescape()浏览器的面向未来的代码。 请注意,IE 9及atob/btoa()版本不支持atob/btoa() ,因此您需要使用自定义的base64函数。

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

UTF-8编码和解码的更全面的例子可以在这里find: http : //jsfiddle.net/47zwb41o/