一份簡明的 Base64 原理解析

書接上回,在 記一個 Base64 有關(guān)的 Bug 一文里,我們說到了 Base64 的編解碼器有不同實現(xiàn),交叉使用它們可能引發(fā)的問題等等。

這一回,我們來對 Base64 這一常用編解碼技術(shù)的原理一探究竟。

1. Base64 是什么

Base64 是一種基于 64 個可打印字符來表示二進(jìn)制數(shù)據(jù)的表示方法。由于 2^6=64,所以每 6 個比特為一個單元,對應(yīng)某個可打印字符。3 個字節(jié)有 24 個比特,對應(yīng)于 4 個 Base64 單元,即 3 個字節(jié)可由 4 個可打印字符來表示。

——維基百科

它不是一種加解密技術(shù),是一種簡單的編解碼技術(shù)。

Base64 常用于表示、傳輸、存儲二進(jìn)制數(shù)據(jù),也可以用于將一些含有特殊字符的文本內(nèi)容編碼,以便傳輸。

比如:

  1. 在電子郵件的傳輸中,Base64 可以用來將 binary 的字節(jié)序列,比如附件,編碼成 ASCII 字節(jié)序列;

  2. 將一些體積不大的圖片 Base64 編碼后,直接內(nèi)嵌到網(wǎng)頁源碼里;

  3. 將要傳遞給 HTTP 請求的參數(shù)做簡單的轉(zhuǎn)換,降低肉眼可讀性;

    注:用于 URL 的 Base64 非標(biāo)準(zhǔn) Base64,是一種變種。

  4. 網(wǎng)友們在論壇等公開場合習(xí)慣將郵箱地址 Base64 后再發(fā)出來,防止被爬蟲抓取后發(fā)送垃圾郵件。

2. Base64 編碼原理

標(biāo)準(zhǔn) Base64 里的 64 個可打印字符是 A-Za-z0-9+/,分別依次對應(yīng)索引值 0-63。索引表如下:

image

編碼時,每 3 個字節(jié)一組,共 8bit*3=24bit,劃分成 4 組,即每 6bit 代表一個編碼后的索引值,劃分如下圖所示:

image

這樣可能不太直觀,舉個例子就容易理解了。比如我們對 cat 進(jìn)行編碼:

image

可以看到 cat 編碼后變成了 Y2F0。

如果待編碼內(nèi)容的字節(jié)數(shù)不是 3 的整數(shù)倍,那需要進(jìn)行一些額外的處理。

如果最后剩下 1 個字節(jié),那么將補 4 個 0 位,編碼成 2 個 Base64 字符,然后補兩個 =

image

如果最后剩下 2 個字節(jié),那么將補 2 個 0 位,編碼成 3 個 Base64 字符,然后補一個 =

image

3. 實現(xiàn)一個簡易的 Base64 編碼器

講完原理,我們就可以動手實現(xiàn)一個簡易的標(biāo)準(zhǔn) Base64 編碼器了,以下是我參考 Java 8 的 java.util.Base64 亂寫的一個 Java 版本,僅供參考,主要功能代碼如下:

public class CustomBase64Encoder {

    /**
     * 索引表
     */
    private static final char[] sBase64 = {
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
            'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
            'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
            'w', 'x', 'y', 'z', '0', '1', '2', '3',
            '4', '5', '6', '7', '8', '9', '+', '/'
    };

    /**
     * 將 byte[] 進(jìn)行 Base64 編碼并返回字符串
     * @param src 原文
     * @return 編碼后的字符串
     */
    public static String encode(byte[] src) {
        if (src == null) {
            return null;
        }

        byte[] dst = new byte[(src.length + 2) / 3 * 4];

        int index = 0;

        // 每次將 3 個字節(jié)編碼為 4 個字節(jié)
        for (int i = 0; i < (src.length / 3 * 3); i += 3) {
            int bits = (src[i] & 0xff) << 16 | (src[i + 1] & 0xff) << 8 | (src[i + 2] & 0xff);
            dst[index++] = (byte) sBase64[(bits >>> 18) & 0x3f];
            dst[index++] = (byte) sBase64[(bits >>> 12) & 0x3f];
            dst[index++] = (byte) sBase64[(bits >>> 6) & 0x3f];
            dst[index++] = (byte) sBase64[bits & 0x3f];
        }

        // 處理剩下的 1 個或 2 個字節(jié)
        if (src.length % 3 == 1) {
            int bits = (src[src.length - 1] & 0xff) << 4;
            dst[index++] = (byte) sBase64[(bits >>> 6) & 0x3f];
            dst[index++] = (byte) sBase64[bits & 0x3f];
            dst[index++] = '=';
            dst[index] = '=';
        } else if (src.length % 3 == 2) {
            int bits = (src[src.length - 2] & 0xff) << 10 | (src[src.length - 1] & 0xff) << 2;
            dst[index++] = (byte) sBase64[(bits >>> 12) & 0x3f];
            dst[index++] = (byte) sBase64[(bits >>> 6) & 0x3f];
            dst[index++] = (byte) sBase64[bits & 0x3f];
            dst[index] = '=';
        }

        return new String(dst);
    }
}

這部分源碼我也上傳到 GitHub 倉庫 https://github.com/mzlogin/spring-practices 的 base64test 工程里了。

4. 其它知識點

4.1 為什么有的編碼結(jié)果帶回車

在電子郵件中,根據(jù) RFC 822 規(guī)定,每 76 個字符需要加上一個回車換行,所以有些編碼器實現(xiàn),比如 sun.misc.BASE64Encoder.encode,是帶回車的,還有 java.util.Base64.Encoder.RFC2045,是帶回車換行的,每行 76 個字符。

4.2 Base64 的變種

除了標(biāo)準(zhǔn) Base64 之外,還有一些其它的 Base64 變種。

比如在 URL 的應(yīng)用場景中,因為標(biāo)準(zhǔn) Base64 索引表中的 /+ 會被 URLEncoder 轉(zhuǎn)義成 %XX 形式,但 % 是 SQL 中的通配符,直接用于數(shù)據(jù)庫操作會有問題。此時可以采用 URL Safe 的編碼器,索引表中的 /+ 被換成 -_,比如 java.util.Base64.Encoder.RFC4648_URLSAFE 就是這樣的實現(xiàn)。

5. 參考鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 為什么要進(jìn)行Base64編碼 Base64最早就是用于郵件傳輸協(xié)議中的,原因是郵件傳輸協(xié)議只支持ASCII字符傳遞...
    Longshihua閱讀 11,716評論 0 11
  • 一、Base64編碼由來 為什么會有Base64編碼呢?因為有些網(wǎng)絡(luò)傳送渠道并不支持所有的字節(jié),例如傳統(tǒng)的郵件只支...
    爐石不傳說閱讀 1,445評論 0 1
  • 為什么會出現(xiàn)Base64 來源:https://www.zhihu.com/question/36306744/a...
    Sraindy閱讀 1,231評論 0 3
  • 1、Base64編碼原理 下圖為Base64編碼索引表: 字符選用了"A-Z、a-z、0-9、+、/" 64個可打...
    M_JCs閱讀 1,876評論 1 9
  • 釋懷山東 歲月更迭,四季輪回,陰晴圓缺,悲歡離合, 花開花落,潮落潮起,情通萬物,萬物有情, 齊魯大地,激情澎湃,...
    琴詩書畫閱讀 175評論 0 0

友情鏈接更多精彩內(nèi)容