阅读 319
今天学到了什么|终于搞明白base64编码了

今天学到了什么|终于搞明白base64编码了

某次和小伙伴对问题,他指着一串字符串问我:你知道这里为什么有两个等号(=)吗?

我:我不知道......

他:因为这是base64编码,@##@%^&*&%#@@#$%^,位数不够所以用=补齐。

我:哦~(点头假装听明白了)

经小伙伴这么一说,我突然想起来以前面试的时候,好像也被面试官问过base64编码相关的问题,当时除了知道这是一种编码方式以及前端会用它进行图片编码之外,对于它具体的编码规则(WHAT、WHY、HOW)都不了解,所以今天就去仔细看了一遍,在这里整理一下吧。BTW,去学习某个知识的原因并不是因为面试会问到,而是因为......学到就是赚到嘛!^ ^

一、WHAT

什么是base64编码?它是基于64个可打印的字符来表示二进制的数据的一种方法,哪64个可打印的字符呢?

['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/'] (26 + 26 + 10 + 2 = 64)

这64个字符可以称作base64的索引表,这是标准的base64协议规定。为什么叫base64呢?因为它是基于这64个可打印字符,用6位2进制数(26=642^6=64)表示字符,因此称为base64。同理可得,base32就是用5位2进制数、base16就是用4位2进制数去表示字符。

二、WHY

为什么要使用这种编码方式呢?它本质上不还是二进制吗?为什么不直接传输二进制,而再用base64的方式去转换呢?

可能有人会觉得base64是用来加密的——但其实并不是,因为如果我们稍微了解一下它的编码规则就会知道,它是很容易能被反向解码出来的。(当然可以通过打乱编码顺序来达到加密的目的,这种情况这里先不讨论)那为什么要用base64编码呢?真正的原因是二进制不兼容。某些二进制值,在一些硬件或软件上,比如在不同的路由器、老电脑、老软件、网络协议上,表示的意义不一样,做的处理也不一样。这就导致那些不兼容的二进制值被错误处理,这肯定是不行的嘛。所以就先把传输数据做一个base64编码,统统变成可见字符(各个软件硬件都兼容的二进制值),这样出错的可能性就大大降低了。

三、HOW

接下来说说base64是怎么编码的呢?

  • STEP 1:将需要转换的字符串每三个字节分为一组,每个字节占8bit,那么共有24个二进制位。(这里为什么要每三个字节一组呢?因为每个字节占8bit,而base64是用6位二进制表示字符,8和6的最小公倍数是24,所以三个字节一组刚刚好~)
  • STEP 2:将上面的24个二进制位每6个一组,共分为4组。
  • STEP 3:在每组前面添加两个0(注意这里,编码之后长度会变长),每组由6个变为8个二进制位,总共32个二进制位,即四个字节。
  • STEP 4:根据以下base64编码对照表获得对应的值。
0 A  17 R   34 i   51 z
1 B  18 S   35 j   52 0
2 C  19 T   36 k   53 1
3 D  20 U   37 l   54 2
4 E  21 V   38 m   55 3
5 F  22 W   39 n   56 4
6 G  23 X   40 o   57 5
7 H  24 Y   41 p   58 6
8 I  25 Z   42 q   59 7
9 J  26 a   43 r   60 8
10 K  27 b   44 s   61 9
11 L  28 c   45 t   62 +
12 M  29 d   46 u   63 /
13 N  30 e   47 v
14 O  31 f   48 w   
15 P  32 g   49 x
16 Q
复制代码

Base64字符表中的字符原本用6个bit就可以表示,现在前面添加2个0,变为8个bit,因此,base64编码之后的文本,要比原文增加三分之一

举个具体的🌰:对“Hah”进行base64编码:

  • “H”、“a”、"h"对应的ASCII码值分别为72,97,104,对应的二进制值是01001000、01100001、01101000。如下图第二三行所示,组成一个24位的二进制字符串。
  • 将24位每6位二进制位一组分成四组。
  • 在上面每一组前面补两个0,扩展成32个二进制位,此时变为四个字节:00010010 、00000110、00000101、00101000。分别对应的值(base64编码索引)为:18 、6、5、40。
  • 用上面的值在base64编码表中进行查找,分别对应:S 、G、F、o。
文本Hah
ASCII码7297104
二进制值010010000110000101101000
6bit一组010010 、000110、000101、101000
base64索引18 、6、5、40
base64编码S 、G、F、o

因此“Hah”base64编码之后就变为:SGFo。

上面我们提到编码的第一步是“每三个字节”一组分组,那要转换的字符没有三个字节怎么办?比如现在要对“A”和“BC”进行base64编码~

image1.png

一个字节:一个字节共8个二进制位,依旧按照规则进行分组。此时共8个二进制位,每6个一组,则第二组缺少4位,用0补齐,得到两个base64编码,而后面两组没有对应数据,都用“=”补上。因此,“A”编码之后为“QQ==”;

两个字节:两个字节共16个二进制位,依旧按照规则进行分组。此时总共16个二进制位,每6个一组,则第三组缺少2位,用0补齐,得到三个base64编码,第四组完全没有数据则用“=”补上。因此,“BC”编码之后为“QKM=”。

(终于明白开头那位小伙伴的意思了~)

四、前端场景——图片编码

我们知道,我们所看到的网页上的每一个图片,都需要消耗一个http请求下载,要是图片的下载不用向服务器发出请求,而可以随着HTML的下载同时下载到本地就好了,base64正好能解决这个问题。

比如一个基于base64编码后的图片地址长这样:

// 我只是一个🌰 data:image/gif;base64,R03t7txgBjboSvB8EpLoFZywOAo3LFE5lYs/QW9LT1TRk1V7S2xYJADs=

我们将其放在HTML或者CSS中,可以不向服务器发出请求就能拿到图片。

需要注意的是,前面我们提到,使用base64有一个问题是编码之后体积会增大1/3,所以使用base64不一定就能提升性能,需要考虑具体的场景。比如,如果图片体积很小且因为用处的特殊性无法被制作成雪碧图,在整个项目的复用性很高且基本不会被改变,那么此时或许可以使用base64编码传输图片(比如页面的背景图)。

文章分类
前端
文章标签