我使用Javascript window.atob()函数来解码base64编码的字符串(特别是来自GitHub API的base64编码的内容)。问题是我得到ascii编码的字符返回(如âⅱ而不是™)。我如何正确地处理传入的base64编码流,以便将其解码为utf-8?


当前回答

以下是Mozilla开发资源中描述的2018年更新的解决方案

从unicode编码到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=="

从b64解码到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"

其他回答

下面是一些面向可能缺少escape/unescape()的浏览器的面向未来的代码。请注意,ie9及以上版本不支持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编码和解码的例子可以在这里找到:http://jsfiddle.net/47zwb41o/

对我有用的完整文章:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding

我们从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"

这是当今最常用的方法之一。

解码base64到UTF8字符串

以下是@brandonscript目前投票最多的答案

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

上面的代码可以工作,但是非常慢。如果您的输入是一个非常大的base64字符串,例如,对于一个base64 html文档,30,000个字符。这需要大量的计算。

这是我的答案,使用内置的TextDecoder,比上面的大输入代码快近10倍。

function decodeBase64(base64) {
    const text = atob(base64);
    const length = text.length;
    const bytes = new Uint8Array(length);
    for (let i = 0; i < length; i++) {
        bytes[i] = text.charCodeAt(i);
    }
    const decoder = new TextDecoder(); // default is utf-8
    return decoder.decode(bytes);
}

如果试图解码节点中utf8编码数据的Base64表示,您可以使用本机Buffer helper

Buffer.from("4pyTIMOgIGxhIG1vZGU=", "base64").toString(); // '✓ à la mode'

Buffer的toString方法默认为utf8,但您可以指定任何所需的编码。例如,相反的操作是这样的

Buffer.from('✓ à la mode', "utf8").toString("base64"); // "4pyTIMOgIGxhIG1vZGU="

事物是变化的。escape/unescape方法已弃用。

你可以在对字符串进行base64编码之前对其进行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应该可以解决这个问题。