Base64编码

原理

什么是base64
  • Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。
为什么会有base64
  • 由于HTTP协议是文本协议,所以在HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符。
什么是“可打印字符”呢?
  • 在ASCII码中规定,031、128这33个字符属于控制字符,32127这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输。
那么该怎么才能传输其他字符呢?
  • 其中一种方式就是使用Base64。Base64一般用于在HTTP协议下传输二进制数据。
可忽略
  1. 完整的base64定义可见 RFC1421和 RFC2045。编码后的数据比原始数据略长,为原来的4/3。在电子邮件中,根据RFC822规定,每76个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1
Table 1: The Base64 Alphabet
索引对应字符索引对应字符索引对应字符索引对应字符
0A17R34i51z
1B18S35j520
2C19T36k531
3D20U37l542
4E21V38m553
5F22W39n564
6G23X40o575
7H24Y41p586
8I25Z42q597
9J26a43r608
10K27b44s619
11L28c45t62+
12M29d46u63/
13N30e47v
14O31f48w
15P32g49x
16Q33h50y
  • 也就是说,如果将索引转换为对应的二进制数据的话需要至多6个Bit(2^6=64)。然而ASCII码需要8个Bit来表示,那么怎么使用6个Bit来表示8个Bit的数据呢?6个Bit当然不能存储8个Bit的数据,但是4*6个Bit可以存储3*8个Bit的数据啊!
    在这里插入图片描述
  • 可以看到“Son”通过Base64编码转换成了“U29u”。这是刚刚好的情况,3个ASCII字符刚好转换成对应的4个Base64字符。但是,当需要转换的字符数不是3的倍数的情况下该怎么办呢?Base64规定,当需要转换的字符不是3的倍数时,一律采用补0的方式凑足3的倍数,具体如下表所示:
    在这里插入图片描述
  • 每6个Bit为一组,第一组转换后为字符“U”,第二组末尾补4个0转换后为字符“w”。剩下的使用“=”替代。即字符“S”通过Base64编码后为“Uw==”。这就是Base64的编码过程。
HTML规范关于base64有哪些API
以下两个函数只能对 **ASCII码** 进行编解码,使用的是拉丁字符集
提示:
  • 拉丁字符集和utf-8前128位字符,都涵盖了ASCII码。所以各种字符集互转,都是将本字符集

window.atob()

  • 对base64编码过的字符串进行解码

  • a: ASCII码 b:Binary

  • atob 意思是通过ASCII码形式表示二进制数,不是将ASCII码转换为二进制数

window.btoa()

  • 对ASCII编码的字符串进行base64编码(不支持汉字,汉字可通过URIencode预处理后再编码)
  • 将ascii码解析成binary数据
注意
  1. 标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符

  2. 为解决此问题,可采用一种用于URL的改进Base64编码,它在末尾填充**’=’**号,并将标准Base64中的“+”和“/”分别改成了“-”和“_”,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。

实现

Base64实现
JavaScript源码版
1. 源码版
const Base64Util = {
  _keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
  encode: function (e) {
    let t = ''
    let n, r, i, s, o, u, a
    let f = 0
    e = Base64Util._utf8_encode(e)
    while (f < e.length) {
      n = e.charCodeAt(f++)
      r = e.charCodeAt(f++)
      i = e.charCodeAt(f++)
      s = n >> 2
      o = (n & 3) << 4 | r >> 4
      u = (r & 15) << 2 | i >> 6
      a = i & 63
      if (isNaN(r)) {
        u = 64
        a = 64
      } else if (isNaN(i)) {
        a = 64
      }
      t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a)
    }
    return t
  },
  decode: function (e) {
    var t = ''
    var n, r, i
    var s, o, u, a
    var f = 0
    // eslint-disable-next-line no-useless-escape
    e = e.replace(/[^A-Za-z0-9\+\/\=]/g, '')
    while (f < e.length) {
      s = this._keyStr.indexOf(e.charAt(f++))
      o = this._keyStr.indexOf(e.charAt(f++))
      u = this._keyStr.indexOf(e.charAt(f++))
      a = this._keyStr.indexOf(e.charAt(f++))
      n = s << 2 | o >> 4
      r = (o & 15) << 4 | u >> 2
      i = (u & 3) << 6 | a
      t = t + String.fromCharCode(n)
      if (u !== 64) {
        t = t + String.fromCharCode(r)
      } if (a !== 64) {
        t = t + String.fromCharCode(i)
      }
    }
    t = Base64Util._utf8_decode(t)
    return t
  },
  _utf8_encode: function (e) {
    e = e.replace(/\r\n/g, 'n')
    let t = ''
    for (let n = 0; n < e.length; n++) {
      let r = e.charCodeAt(n)
      // ASCII码
      if (r < 128) {
        t += String.fromCharCode(r)
      } else if (r > 127 && r < 2048) {
        t += String.fromCharCode(r >> 6 | 192)
        t += String.fromCharCode(r & 63 | 128)
      } else {
        t += String.fromCharCode(r >> 12 | 224)
        t += String.fromCharCode(r >> 6 & 63 | 128)
        t += String.fromCharCode(r & 63 | 128)
      }
    }
    return t
  },
  _utf8_decode: function (e) {
    var t = ''
    var n = 0
    var r = 0
    var c1 = 0
    var c2 = 0
    while (n < e.length) {
      r = e.charCodeAt(n)
      if (r < 128) {
        t += String.fromCharCode(r)
        n++
      } else if (r > 191 && r < 224) {
        c1 = e.charCodeAt(n + 1)
        t += String.fromCharCode((r & 31) << 6 | c1 & 63)
        n += 2
      } else {
        c1 = e.charCodeAt(n + 1)
        c2 = e.charCodeAt(n + 2)
        t += String.fromCharCode((r & 15) << 12 | (c1 & 63) << 6 | c2 & 63)
        n += 3
      }
    }
    return t
  }
}

2. api版 不推荐, 
// 使用utf-8字符集进行base64编码
function utoa(str) {
    // 1. encodeURIComponent只能将中文加密,不可以加密ascii, 将utf-8字符串 转化为 16进制字符串
	// 2. unescape将 16进制序列 转换为 拉丁字符集
	// 3. btoa 将拉丁字符集 用base64 编码
    return btoa(unescape(encodeURIComponent(str)));
}
// 使用utf-8字符集解析base64字符串 
function atou(str) {
	// 1. atob 将 base64编码 转化为 拉丁字符集 
	// 2. unescape将 拉丁字符集 转换为 16进制序列
    // 3. decodeURIComponent只能将中文解密,不可以加密ascii, 将16进制字符串 转化为 utf-8字符集 
    return decodeURIComponent(escape(window.atob(str)));
}
java版
import java.util.Base64;
对于标准的Base64:
加密为字符串使用Base64.getEncoder().encodeToString();
加密为字节数组使用Base64.getEncoder().encode();
解密使用Base64.getDecoder().decode();
对于URL安全或MIME的Base64,只需将上述getEncoder()getDecoder()更换为getUrlEncoder()getUrlDecoder()getMimeEncoder()getMimeDecoder()即可。

应用场景

base64有哪些应用场景
  1. 前端将较小的icon编码为base64直接在文档中加载,减少http请求次数
  2. 电子邮件传输二进制文件时,通常用base64编码后再传

注意:

  1. base64编码后的数据量是要比编码前大的,所以base64不能用于减少数据量。
  2. base64不能用于加密数据,即使使用私有的索引表也是不安全的,只是一种编码方式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值