zoukankan      html  css  js  c++  java
  • Base64编码原理及应用

    最近在做一个H5上传图片并压缩的项目,其过程主要是先将图片上传通过readAsDataURL获取上传图片base64编码,然后根据高宽比将图片画到canvas上实现压缩,在通过toDataURL获取压缩后的图片。点击可查看demo在该过程中用到base64编码,于是就想弄清楚base64编码原理,才有了这篇博客。

    Base64编码的来历

    为什么会有Base64编码呢?因为有些网络传送渠道并不支持所有的字节,例如传统的邮件只支持可见字符的传送,像ASCII码的控制字符就不能通过邮件传送。这样用途就受到了很大的限制,比如图片二进制流的每个字节不可能全部是可见字符,所以就传送不了。最好的方法就是在不改变传统协议的情况下,做一种扩展方案来支持二进制文件的传送。把不可打印的字符也能用可打印字符来表示,问题就解决了。Base64编码应运而生,Base64编码就是一种基于64个可打印字符来表示二进制数据的表示方法。

    Base64编码过程

    1. 将每三个字节作为一组,一共是24个二进制位。
    2. 将这24个二进制位分为四组,每个组有6个二进制位。
    3. 在每组前面加两个00,扩展成32个二进制位,即四个字节。
    4. 根据下表,得到扩展后的每个字节的对应符号,这就是Base64的编码值。

    Base64编码表

    key char key char key char key char
    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 33 h 50 y

    字符长度为能被3整除时,比如Man

                M           a           n
    ASCII:      77          97          110
    8bit字节:    01001101    01100001    01101110
    6bit字节:      010011      010110      000101      101110
    十进制:      19          22          5            46
    对应编码:     T           W           F            u
    
    1. "M"、"a"、"n"的ASCII值分别是77、97、110,对应的二进制值是01001101、01100001、01101110,将它们连成一个24位的二进制字符串010011010110000101101110。
    2. 将这个24位的二进制字符串分成4组,每组6个二进制位:010011、010110、000101、101110。
    3. 在每组前面加两个00,扩展成32个二进制位,即四个字节:00010011、00010110、00000101、00101110。它们的十进制值分别是19、22、5、46。
    4. 根据上表,得到每个值对应Base64编码,即T、W、F、u。

    字符串长度不能被3整除时,比如Lucy:

                L           u           c           y
    ASCII:      76          117         99          121
    8bit字节:    01001100    01110101    01100011    01111001      00000000    00000000
    6bit字节:      010011      000111      010101      100011        011110      010000      000000  000000
    十进制:      19          7           21          35            30          16             (补0) (补0)      
    对应编码:     T           H           V           j             e           Q            =       =
    
    

    如果要编码的字节数不能被3整除,最后会多出1个或2个字节,那么可以使用下面的方法进行处理:
    先使用0字节值在末尾补足,使其能够被3整除,然后再进行Base64的编码。在编码后的Base64文本后加上一个或两个=号,代表补足的字节数。也就是说,当最后剩余一个八位字节(1个byte)时,最后一个6位的Base64字节块有四位是0值,最后附加上两个等号;如果最后剩余两个八位字节(2个byte)时,最后一个6位的base字节块有两位是0值,最后附加一个等号。

    因为,Base64将三个字节转化成四个字节,因此Base64编码后的文本,会比原文本大出三分之一左右。

    注意
    Base64编码主要用在传输、存储、表示二进制领域,不能算得上加密,只是无法直接看到明文,不建议用base64编码用于加密。
    中文有多种编码(比如:utf-8、gb2312、gbk等),不同编码对应Base64编码结果都不一样。

    base64实现

    在PHP中,有一对专门的函数用于Base64转换:base64_encode()用于编码、base64_decode()用于解码。
    在Javascript中可使用window.btoa()用于编码,window.atob()用于解码。 兼容性可点击查看IE10及以上全部兼容
    对于IE10以下可采用以下兼容方法

    /**
     * base64 encoding & decoding
     * for fixing browsers which don't support Base64 | btoa |atob
     */
    
    (function (win, undefined) {
    
        var Base64 = function () {
            var base64hash = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    
            // btoa method
            function _btoa(s) {
                if (/([^u0000-u00ff])/.test(s)) {  // 排除中文
                    throw new Error('INVALID_CHARACTER_ERR');
                }
                var i = 0,
                    prev,
                    ascii,
                    mod,
                    result = [];
    
                while (i < s.length) {
                    ascii = s.charCodeAt(i);
                    mod = i % 3;
    
                    switch (mod) {
                        // 第一个6位只需要让8位二进制右移两位
                        case 0:
                            result.push(base64hash.charAt(ascii >> 2));
                            break;
                        //第二个6位 = 第一个8位的后两位 + 第二个8位的前4位
                        case 1:
                            result.push(base64hash.charAt((prev & 3) << 4 | (ascii >> 4)));
                            break;
                        //第三个6位 = 第二个8位的后4位 + 第三个8位的前2位
                        //第4个6位 = 第三个8位的后6位
                        case 2:
                            result.push(base64hash.charAt((prev & 0x0f) << 2 | (ascii >> 6)));
                            result.push(base64hash.charAt(ascii & 0x3f));
                            break;
                    }
    
                    prev = ascii;
                    i++;
                }
    
                // 循环结束后看mod, 为0 证明需补3个6位,第一个为最后一个8位的最后两位后面补4个0。另外两个6位对应的是异常的“=”;
                // mod为1,证明还需补两个6位,一个是最后一个8位的后4位补两个0,另一个对应异常的“=”
                if (mod == 0) {
                    result.push(base64hash.charAt((prev & 3) << 4));
                    result.push('==');
                } else if (mod == 1) {
                    result.push(base64hash.charAt((prev & 0x0f) << 2));
                    result.push('=');
                }
    
                return result.join('');
            }
    
            // atob method
            // 逆转encode的思路即可
            function _atob(s) {
                s = s.replace(/s|=/g, '');
                var cur,
                    prev,
                    mod,
                    i = 0,
                    result = [];
    
                while (i < s.length) {
                    cur = base64hash.indexOf(s.charAt(i));
                    mod = i % 4;
    
                    switch (mod) {
                        case 0:
                            //TODO
                            break;
                        case 1:
                            result.push(String.fromCharCode(prev << 2 | cur >> 4));
                            break;
                        case 2:
                            result.push(String.fromCharCode((prev & 0x0f) << 4 | cur >> 2));
                            break;
                        case 3:
                            result.push(String.fromCharCode((prev & 3) << 6 | cur));
                            break;
                    }
    
                    prev = cur;
                    i++;
                }
    
                return result.join('');
            }
    
            return {
                btoa: _btoa,
                atob: _atob,
                encode: _btoa,
                decode: _atob
            };
        }();
    
        if (!win.Base64) { win.Base64 = Base64 }
        if (!win.btoa) { win.btoa = Base64.btoa }
        if (!win.atob) { win.atob = Base64.atob }
    
    })(window)
    

    Base64实际运用

    1. 用base64引入图片,当图片大小较小时可直接将其用base64编码,这样可以减少请求,目前webpack中可使用url-loader处理图片
    2. 在邮件中使用base64编码
    参考文章
    1. Base64
    2. Base64笔记
    3. 关于base64编码的原理及实现
  • 相关阅读:
    npm 之 --save , -D,--save -dev的区别
    webpack 之 打包(最新版)
    npm 与 yarn 对比
    webpack 之 打包图片文件
    webpack 之 打包less文件
    javascript 之 Event Loop
    package.json中type的含义
    webpack 之 打包css文件操作
    常见问题 之 webpack打包css问题
    类方法和对象方法
  • 原文地址:https://www.cnblogs.com/jesse131/p/11529958.html
Copyright © 2011-2022 走看看