window.btoa和window.atob,是浏览器提供的字符串base64编解码。
但是该方法不支持中文字符串的转码。因为转码是针对ASCII码。

可以如下操作实现

window.btoa(window.encodeURIComponent(str))
window.decodeURIComponent(window.atob(str))

先将中文编码,然后再转base64

注意

无论是escape,encodeURIComponent,encodeURI那种方法,都不是专门针对中文的。
所以,以上方法也会将一些特殊符号@*/+等转码
结果就是除了中文以外的其他字符最终结果和直接btoa的结果不一致。

这里就要看使用场景,只是前端使用没有问题,如果涉及到后台等第三方就不能用了。
常用的js-base插件方法:encode,
原理如下

  • 将字符串中非acsill字符按固定规则转为两个或者三个长度的UTF-16码

    • 关键就是这段代码
    const cb_utob = (c) => {
       if (c.length < 2) {
           var cc = c.charCodeAt(0);
           return cc < 0x80 ? c
               : cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6))
                   + _fromCC(0x80 | (cc & 0x3f)))
                   : (_fromCC(0xe0 | ((cc >>> 12) & 0x0f))
                       + _fromCC(0x80 | ((cc >>> 6) & 0x3f))
                       + _fromCC(0x80 | (cc & 0x3f)));
       }
       else {
           var cc = 0x10000
               + (c.charCodeAt(0) - 0xD800) * 0x400
               + (c.charCodeAt(1) - 0xDC00);
           return (_fromCC(0xf0 | ((cc >>> 18) & 0x07))
               + _fromCC(0x80 | ((cc >>> 12) & 0x3f))
               + _fromCC(0x80 | ((cc >>> 6) & 0x3f))
               + _fromCC(0x80 | (cc & 0x3f)));
       }
    };
    const re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
    //utf-8 string return utf-16字符串
    const utob = (u) => u.replace(re_utob, cb_utob);
  • 然后就可以转base64了
const btoaPolyfill = (bin) => {
   // console.log('polyfilled');
   let u32, c0, c1, c2, asc = '';
   const pad = bin.length % 3;
   for (let i = 0; i < bin.length;) {
       if ((c0 = bin.charCodeAt(i++)) > 255 ||
           (c1 = bin.charCodeAt(i++)) > 255 ||
           (c2 = bin.charCodeAt(i++)) > 255)
           throw new TypeError('invalid character found');
       u32 = (c0 << 16) | (c1 << 8) | c2;
       asc += b64chs[u32 >> 18 & 63]
           + b64chs[u32 >> 12 & 63]
           + b64chs[u32 >> 6 & 63]
           + b64chs[u32 & 63];
   }
   return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
};
  • 这个方法是插件自己的转base64方法,每个base64字符代表3个bit,方法其实我没太看懂

mdn还有另一种方法

  • 原理字符串直接映射ArrayBuffer二进制数组

代码和地址如下:

  /* Array of bytes to base64 string decoding */

function b64ToUint6 (nChr) {

  return nChr > 64 && nChr < 91 ?
      nChr - 65
    : nChr > 96 && nChr < 123 ?
      nChr - 71
    : nChr > 47 && nChr < 58 ?
      nChr + 4
    : nChr === 43 ?
      62
    : nChr === 47 ?
      63
    :
      0;

}

function base64DecToArr (sBase64, nBlockSize) {

  var
    sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
    nOutLen = nBlockSize ? Math.ceil((nInLen * 3 + 1 >>> 2) / nBlockSize) * nBlockSize : nInLen * 3 + 1 >>> 2, aBytes = new Uint8Array(nOutLen);

  for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
    nMod4 = nInIdx & 3;
    nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
    if (nMod4 === 3 || nInLen - nInIdx === 1) {
      for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
        aBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
      }
      nUint24 = 0;
    }
  }

  return aBytes;
}

/* Base64 string to array encoding */

function uint6ToB64 (nUint6) {

  return nUint6 < 26 ?
      nUint6 + 65
    : nUint6 < 52 ?
      nUint6 + 71
    : nUint6 < 62 ?
      nUint6 - 4
    : nUint6 === 62 ?
      43
    : nUint6 === 63 ?
      47
    :
      65;

}

function base64EncArr (aBytes) {

  var eqLen = (3 - (aBytes.length % 3)) % 3, sB64Enc = "";

  for (var nMod3, nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
    nMod3 = nIdx % 3;
    /* Uncomment the following line in order to split the output in lines 76-character long: */
    /*
    if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; }
    */
    nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24);
    if (nMod3 === 2 || aBytes.length - nIdx === 1) {
      sB64Enc += String.fromCharCode(uint6ToB64(nUint24 >>> 18 & 63), uint6ToB64(nUint24 >>> 12 & 63), uint6ToB64(nUint24 >>> 6 & 63), uint6ToB64(nUint24 & 63));
      nUint24 = 0;
    }
  }

  return  eqLen === 0 ?
      sB64Enc
    :
      sB64Enc.substring(0, sB64Enc.length - eqLen) + (eqLen === 1 ? "=" : "==");

}

MDN-base64


肥皂泡
382 声望6 粉丝

码农