<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          前端Base64編碼知識(shí),一文打盡

          共 17782字,需瀏覽 36分鐘

           ·

          2021-08-22 23:51

          原文: https://juejin.cn/post/6989391487200919566 
          作者: 云的世界 
          掘金專(zhuān)欄: 前端基礎(chǔ)進(jìn)階



          健康滿(mǎn)分



          關(guān)注并將「趣談前端」設(shè)為星標(biāo)

          每天定時(shí)分享技術(shù)干貨/優(yōu)秀開(kāi)源/技術(shù)思維

          前言

          本文將詳細(xì)的介紹前端 Base64 編碼知識(shí),探索起源,讓大家對(duì)開(kāi)發(fā)經(jīng)常用到的 Base64 有個(gè)更全面深入的認(rèn)知。

          大綱

          • Base64在前端的應(yīng)用
          • Base64數(shù)據(jù)編碼起源
          • Base64編碼64的含義
          • Base64編碼優(yōu)缺點(diǎn)
          • 一些計(jì)算機(jī)和前端基礎(chǔ)知識(shí)
          • ASCII碼, Unicode , UTF-8
          • Base64編碼和解碼
          • 其他的成熟方案

          Base64在前端的應(yīng)用

          Base64編碼,你一定知道的,先來(lái)看看它在前端的一些常見(jiàn)應(yīng)用:
          當(dāng)然絕部分場(chǎng)景都是基于Data URLs[1]

          Canvas圖片生成

          canvas的 toDataURL[2]可以把canvas的畫(huà)布內(nèi)容轉(zhuǎn)base64編碼格式包含圖片展示的 data URI[3]。

          const ctx = canvasEl.getContext("2d");
          // ...... other code
          const dataUrl = canvasEl.toDataURL();

          // data:image/png;base64,iVBORw0KGgoAAAANSUhE.........

          你畫(huà)我猜,新用戶(hù)加入,要獲取當(dāng)前的最新的繪畫(huà)界面,也可以通過(guò)Base64格式的消息傳遞。

          文件讀取

          FileReader的 readAsDataURL[4]可以把上傳的文件轉(zhuǎn)為base64格式的data URI,比較常見(jiàn)的場(chǎng)景是用戶(hù)頭像的剪裁和上傳。

          function readAsDataURL() {
              const fileEl = document.getElementById("inputFile");
              return new Promise((resolve, reject) => {
                  const fd = new FileReader();
                  fd.readAsDataURL(fileEl.files[0]);
                  fd.onload = function () {
                      resolve(fd.result);
                      // data:image/png;base64,iVBORw0KGgoAAAA.......
                  }
                  fd.onerror = reject;
              });
          }

          jwt

          jwt由header, payload,signature三部分組成,前兩個(gè)解碼后,都是可以明文看見(jiàn)的。拿 國(guó)服最強(qiáng)JWT生成Token做登錄校驗(yàn)講解,看完保證你學(xué)會(huì)![5] 里面的token做測(cè)試。

          image.png

          網(wǎng)站圖片和小圖片

          移動(dòng)端網(wǎng)站圖標(biāo)優(yōu)化

          <link rel="icon" href="data:," />
          <link rel="icon" href="data:;base64,=" />

          至于怎么獲得這個(gè)值data:,的:

          <canvas height="0" width="0" id="canvas"></canvas>
          <script>
              const canvasEl = document.getElementById("canvas");
              const ctx = canvasEl.getContext("2d");
              dataUrl = canvasEl.toDataURL();
              console.log(dataUrl);  // data:,
          </script>

          小圖片

          這個(gè)就有很多場(chǎng)景了,比如img標(biāo)簽,背景圖等

          img標(biāo)簽:

          <img src="data:image/png;base64,iVBORw0KGgoAAAA......." />

          css背景圖:

          .bg{
              background: url(data:image/png;base64,iVBORw0KGgoAAAA.......)
          }

          簡(jiǎn)單的數(shù)據(jù)加密

          當(dāng)然這不是好方法,但是至少讓你不好解讀。


            const username = document.getElementById("username").vlaue; 
            const password = document.getElementById("password").vlaue;  
            const secureKey = "%%S%$%DS)_sdsdj_66";
            const sPass = utf8_to_base64(password + secureKey);
            
            doLogin({
                username,
                password: sPass
            })
            

          SourceMap

          借用阮大神的一段代碼, 注意mappings字段,這實(shí)際上就是bas64編碼格式的內(nèi)容,當(dāng)然你直接去解,是會(huì)失敗的。

          {
              version : 3,
              file: "out.js",
              sourceRoot : "",
              sources: ["foo.js""bar.js"],
              names: ["src""maps""are""fun"],
              mappings: "AAgBC,SAAQ,CAAEA"
            }

          具體的實(shí)現(xiàn)請(qǐng)看官方的base64-vlq.js[6]文件。

          混淆加密代碼

          著名的代碼混淆庫(kù), javascript-obfuscator[7],其也是有應(yīng)用base64幾碼的,一起看看選項(xiàng):
          webpack-obfuscator[8]也是基于其封裝的。

              --string-array-indexes-type '<list>' (comma separated) [hexadecimal-number, hexadecimal-numeric-string]
              --string-array-encoding '<list>' (comma separated) [none, base64, rc4]
              --string-array-index-shift <boolean>
              --string-array-wrappers-count <number>
              --string-array-wrappers-chained-calls <boolean>

          image.png

          其他

          X.509公鑰證書(shū), github SSH key, mht文件,郵件附件等等,都有Base64的影子。

          Base64數(shù)據(jù)編碼起源

          早期郵件傳輸協(xié)議基于 ASCII 文本,對(duì)于諸如圖片、視頻等二進(jìn)制文件處理并不好。ASCII 主要用于顯示現(xiàn)代英文,到目前為止只定義了 128 個(gè)字符,包含控制字符和可顯示字符。為了解決上述問(wèn)題,Base64 編碼順勢(shì)而生。

          Base64是編解碼,主要的作用不在于安全性,而在于讓內(nèi)容能在各個(gè)網(wǎng)關(guān)間無(wú)錯(cuò)的傳輸,這才是Base64編碼的核心作用。

          除了Base64數(shù)據(jù)編碼,其實(shí)還有Base32數(shù)據(jù)編碼, Base16數(shù)據(jù)編碼,可以參見(jiàn) RFC 4648[9]

          Base64編碼64的含義

          64就是64個(gè)字符的意思。

          base64對(duì)照表, 借用 Base64原理[10]的一張圖:

          1. A-Z 26
          2. a-z 26
          3. 0-9 10
          4. + / 2

          26 + 26 + 10 + 2 = 64

          當(dāng)然還有一個(gè)字符=,這是填充字符,后面會(huì)提到,不屬于64里面的范疇。

          對(duì)照表的索引值,注意一下,后面的base64編碼和解碼會(huì)用到。

          Base64編碼優(yōu)缺點(diǎn)

          優(yōu)點(diǎn)

          1. 可以將二進(jìn)制數(shù)據(jù)(比如圖片)轉(zhuǎn)化為可打印字符,方便傳輸數(shù)據(jù)
          2. 對(duì)數(shù)據(jù)進(jìn)行簡(jiǎn)單的加密,肉眼是安全的
          3. 如果是在html或者css處理圖片,可以減少http請(qǐng)求

          缺點(diǎn)

          1. 內(nèi)容編碼后體積變大, 至少1/3
            因?yàn)槭侨止?jié)變成四個(gè)字節(jié),當(dāng)只有一個(gè)字節(jié)的時(shí)候,也至少會(huì)變成三個(gè)字節(jié)。
          2. 編碼和解碼需要額外工作量

          說(shuō)完優(yōu)缺點(diǎn),回到正題:

          我們今天的重點(diǎn)是 uf8編碼轉(zhuǎn)Base64編碼:

          基本流程

          char => 碼點(diǎn) => utf-8編碼 => base64編碼

          在之前要解一下編碼的知識(shí), 了解編碼知識(shí),又要先了解一些計(jì)算機(jī)的基礎(chǔ)知識(shí)。

          一些計(jì)算機(jī)和前端基礎(chǔ)知識(shí)

          比特和字節(jié)

          比特又叫位。在計(jì)算機(jī)的世界里,信息的表示方式只有 0 和 1, 其可以表示兩種狀態(tài)。
          一位二進(jìn)制可以表示兩狀態(tài), N位可以表示2^N種狀態(tài)。

          一個(gè)字節(jié)(Byte)有8位(Bit)

          所以一個(gè)字節(jié)可以表示 2^8 = 256種狀態(tài);

          獲得字符的 Unicode碼點(diǎn)

          String.prototype.charCodeAt[11] 可以獲取字符的碼點(diǎn),獲取范圍為0 ~ 65535。這個(gè)地方注意一下,關(guān)系到后面的utf-8字節(jié)數(shù)。

          "a".charCodeAt(0)  // 97
          "中".charCodeAt(0) // 20013

          進(jìn)制表示

          1. 0b開(kāi)頭,可以表示二進(jìn)制

          注意0b10000000= 128 ,0b11000000=92,之后會(huì)用到.

          0b11111111 // 255
          0b10000000 // 128 后面會(huì)用到
          0b11000000 // 192 后面會(huì)用到

          image.png
          1. 0x開(kāi)頭,可以表示16進(jìn)制
          0x11111111 // 286331153

          image.png

          0o開(kāi)頭可以表示8進(jìn)制,就不多說(shuō)了,本來(lái)不會(huì)涉及。

          進(jìn)制轉(zhuǎn)換

          10進(jìn)制轉(zhuǎn)其他進(jìn)制
          Number.prototype.toString(radix)[12]可以把十進(jìn)制轉(zhuǎn)為其他進(jìn)制。

          100..toString(2)  // 1100100
          100..toString(16) // 64, 也等于 ox64

          其他進(jìn)制轉(zhuǎn)為10進(jìn)制
          parseInt(string, radix)[13]可以把其他進(jìn)制,轉(zhuǎn)為10進(jìn)制。

          parseInt("10000000", 2) // 128
          parseInt("10",16) // 16

          這里額外提一下一元操作符號(hào)+可以把字符串轉(zhuǎn)為數(shù)字,后面也會(huì)用到,之前提到的0b,0o,0x這里都會(huì)生效。

          +"1000" // 1000
          +"0b10000000" // 128
          +"0o10" // 8
          +"0x10" // 16

          位移操作

          本文只涉及右移操作,就只講右移,右移相當(dāng)于除以2,如果是整數(shù),簡(jiǎn)單說(shuō)是去低位,移動(dòng)幾位去掉幾位,其實(shí)和10進(jìn)制除以10是一樣的。

          64 >> 2 = 16 我們一起看一下過(guò)程

          0 1 0 0 0 0 0 0       64
          -------------------
             0 1 0 0 0 0 | 0 0  16

          一元 & 操作和 一元|操作

          一元&

          當(dāng)兩者皆為1的時(shí)候,值為1。 本文的作用可用來(lái)去高位, 具體看代碼。
          3553 & 36 = 0b110111100001 & 0b111111 = 100001
          因?yàn)楦呶蝗笔В豢赡芏紴?,故均為0, 而低位相當(dāng)于復(fù)制一遍而已。

          110111 100001
                 111111
          ------------
          000000 100001


          一元|
          當(dāng)任意一個(gè)為1,就輸出為1. 本文用來(lái)填補(bǔ)0。比如,把3補(bǔ)成8位二進(jìn)制
          3 | 256 = 11 | 100000000 = 100000011

          100000011.substring(1)是不是就等于8位二進(jìn)制呢00000011

          具備了這些基本知識(shí),我們就開(kāi)始先了解編碼相關(guān)的知識(shí)。

          ASCII碼, Unicode , UTF-8

          ASCII碼

          ASCII碼第一位始終是0, 那么實(shí)際可以表示的狀態(tài)是 2^7 = 128種狀態(tài)。

          ASCII 主要用于顯示現(xiàn)代英文,到目前為止只定義了 128 個(gè)字符,包含控制字符和可顯示字符。

          • 0~31 之間的ASCII碼常用于控制像打印機(jī)一樣的外圍設(shè)備
          • 32~127 之間的ASCII碼表示的符號(hào),在我們的鍵盤(pán)上都可以被找到

          完整的 ASCII碼對(duì)應(yīng)表,可以參見(jiàn) 基本ASCII碼和擴(kuò)展ASCII碼[14]

          接下來(lái)是Unicode和UTF-8編碼,請(qǐng)先記住這個(gè)重要的知識(shí):

          • Unicode: 字符集
          • UTF-8: 編碼規(guī)則

          Unicode

          Unicode 為世界上所有字符都分配了一個(gè)唯一的編號(hào)(碼點(diǎn)),這個(gè)編號(hào)范圍從 0x000000 到 0x10FFFF (十六進(jìn)制),有 100 多萬(wàn),每個(gè)字符都有一個(gè)唯一的 Unicode 編號(hào),這個(gè)編號(hào)一般寫(xiě)成 16 進(jìn)制,在前面加上 U+。例如:的 Unicode 是U+6398。

          • U+0000到U+FFFF

          最前面的65536個(gè)字符位,它的碼點(diǎn)范圍是從0一直到216-1。所有最常見(jiàn)的字符都放在這里。

          • U+010000一直到U+10FFFF

          剩下的字符都放著這里,碼點(diǎn)范圍從U+010000一直到U+10FFFF。

          Unicode有平面的概念,這里就不拓展了。

          Unicode只規(guī)定了每個(gè)字符的碼點(diǎn),到底用什么樣的字節(jié)序表示這個(gè)碼點(diǎn),就涉及到編碼方法。

          UTF-8

          UTF-8 是互聯(lián)網(wǎng)使用最多的一種 Unicode 的實(shí)現(xiàn)方式。還有 UTF-16(字符用兩個(gè)字節(jié)或四個(gè)字節(jié)表示)和 UTF-32(字符用四個(gè)字節(jié)表示)等實(shí)現(xiàn)方式。

          UTF-8 是它是一種變長(zhǎng)的編碼方式, 使用的字節(jié)個(gè)數(shù)從 1 到 4 個(gè)不等,最新的應(yīng)該不止4個(gè), 這個(gè)1-4不等,是后面編碼和解碼的關(guān)鍵。

          UTF-8的編碼規(guī)則:

          1. 對(duì)于只有一個(gè)字節(jié)的符號(hào),字節(jié)的第一位設(shè)為0,后面 7 位為這個(gè)符號(hào)的 Unicode 碼。此時(shí),對(duì)于英語(yǔ)字母UTF-8 編碼和 ASCII 碼是相同的。
          2. 對(duì)于 n 字節(jié)的符號(hào)(n > 1),第一個(gè)字節(jié)的前 n 位都設(shè)為 1,第 n + 1 位設(shè)為0,后面字節(jié)的前兩位一律設(shè)為 10。剩下的沒(méi)有提及的二進(jìn)制位,全部為這個(gè)符號(hào)的 Unicode 碼,如下表所示:
          Unicode 碼點(diǎn)范圍(十六進(jìn)制)十進(jìn)制范圍UTF-8 編碼方式(二進(jìn)制)字節(jié)數(shù)
          0000 0000 ~ 0000 007F0 ~ 1270xxxxxxx1
          0000 0080 ~ 0000 07FF128 ~ 2047110xxxxx 10xxxxxx2
          0000 0800 ~ 0000 FFFF2048 ~ 655351110xxxx 10xxxxxx 10xxxxxx3
          0001 0000 ~ 0010 FFFF65536 ~ 111411111110xxx 10xxxxxx 10xxxxxx 10xxxxxx4

          我們可能沒(méi)見(jiàn)過(guò)字節(jié)數(shù)為2或者為4的字符, 字節(jié)數(shù)為2的可以去Unicode對(duì)應(yīng)表[15]這里找,而等于4的可以去這看看Unicode? 13.0 Versioned Charts Index[16]

          下面這些碼點(diǎn)都處于0000 0080 ~ 0000 07FF, utf-8編碼需要2個(gè)字節(jié)

          下面這些碼點(diǎn)都處于0001 0000 ~ 0010 FFFF, utf-8編碼需要4個(gè)字節(jié)

          可能這里光說(shuō)不好理解,我們分別以英文字符a和中文字符來(lái)講解一下:

          為了驗(yàn)證結(jié)果,可以去 Convert UTF8 to Binary Bits - Online UTF8 Tools[17]

          英文字符a

          1. 先獲得其碼點(diǎn),"a".charCodeAt(0) 等于 97
          2. 對(duì)照表格, 0~127, 需1個(gè)字節(jié)
          3. 97..toString(2) 得到編碼 1100001
          4. 根據(jù)格式0xxxxxxx進(jìn)行填充, 最終結(jié)果
          01100001

          中文字符

          1. 先獲得其碼點(diǎn),"掘".charCodeAt(0) 等于 25496
          2. 對(duì)照表格,2048 ~ 65535 需3個(gè)字節(jié)
          3. 25496..toString(2) 得到編碼 110 001110 011000
          4. 根絕格式1110xxxx 10xxxxxx 10xxxxxx進(jìn)行填充, 最終結(jié)果如下
          11100110 10001110 10011000

          Convert UTF8 to Binary Bits - Online UTF8 Tools[18]執(zhí)行結(jié)果:完全匹配

          image.png

          抽象把字符轉(zhuǎn)為utf8格式二進(jìn)制的方法

          基于上面的表格和轉(zhuǎn)換過(guò)程,我們抽象一個(gè)方法,這個(gè)方法在之后的Base64編碼和解碼至關(guān)重要

          先看看功能,覆蓋utf8編碼1-3字節(jié)范圍

          console.log(to_binary("A"))  // 11100001
          console.log(to_binary("?"))  // 1101100010110011
          console.log(to_binary("掘")) // 111001101000111010011000

          方法如下

          function to_binary(str) {
            const string = str.replace(/\r\n/g, "\n");
            let result = "";
            let code;
            for (var n = 0; n < string.length; n++) {
              //獲取麻點(diǎn)
              code = str.charCodeAt(n);
              if (code < 0x007F) { // 1個(gè)字節(jié)
                // 0000 0000 ~ 0000 007F  0 ~ 127 1個(gè)字節(jié)
                
                // (code | 0b100000000).toString(2).slice(1)
                result += (code).toString(2).padStart(8, '0'); 
              } else if ((code > 0x0080) && (code < 0x07FF)) {
                // 0000 0080 ~ 0000 07FF 128 ~ 2047 2個(gè)字節(jié)
                // 0x0080 的二進(jìn)制為 10000000 ,8位,所以大于0x0080的,至少有8位
                // 格式 110xxxxx 10xxxxxx     

                // 高位 110xxxxx
                result += ((code >> 6) | 0b11000000).toString(2);
                // 低位 10xxxxxx
                result += ((code & 0b111111) | 0b10000000).toString(2);
              } else if (code > 0x0800 && code < 0xFFFF) {
                // 0000 0800 ~ 0000 FFFF 2048 ~ 65535 3個(gè)字節(jié)
                // 0x0800的二進(jìn)制為 1000 00000000,12位,所以大于0x0800的,至少有12位
                // 格式 1110xxxx 10xxxxxx 10xxxxxx

                // 最高位 1110xxxx
                result += ((code >> 12) | 0b11100000).toString(2);  
                // 第二位 10xxxxxx
                result += (((code >> 6) & 0b111111) | 0b10000000).toString(2);
                // 第三位 10xxxxxx
                result += ((code & 0b111111) | 0b10000000).toString(2);
              } else {
                // 0001 0000 ~ 0010 FFFF   65536 ~ 1114111   4個(gè)字節(jié) 
                // https://www.unicode.org/charts/PDF/Unicode-13.0/U130-2F800.pdf
                throw new TypeError("暫不支持碼點(diǎn)大于65535的字符")
              }
            }
            return result;
          }


          方法中有三個(gè)地方稍微難理解一點(diǎn),我們一起來(lái)解讀一下:

          1. 二字節(jié) (code >> 6) | 0b11000000

          其作用是生成高位二進(jìn)制。
          我們以實(shí)際的一個(gè)栗子來(lái)講解,以?為例,其碼點(diǎn)為0x633,在0000 0080 ~ 0000 07FF之間,占兩個(gè)字節(jié), 在其二進(jìn)制編碼為11 000110011 , 其填充格式如下, 低位要用6位。

          110xxxxx 10xxxxxx

          為了方便觀察,我們把 11 000110011 重新調(diào)整一下 11000 110011。

          (code >> 6) 等于 00110011 >> 6,右移6位, 直接干掉低6位。為什么是6呢,因?yàn)榈臀恍枰?位,右移動(dòng)6位后,剩下的就是用于高位操作的位了。

          11000000
             11000 | 110011 
          --------------
          11011000      

          1. 二字節(jié) (code & 0b111111) | 0b10000000
            作用,用于生成低位二進(jìn)制。以?為例,11000 110011, 填充格式
            110xxxxx 10xxxxxx

          (code & 0b111111)這步的操作是為了干掉6位以上的高位,僅僅保留低6位。一元&符號(hào),兩邊都是1的時(shí)候才會(huì)是1,妙啊。

          11000 110011
                111111
          ------------------
                110011  

          接著進(jìn)行 | 0b10000000, 主要是按照格式10xxxxxx進(jìn)行位數(shù)填補(bǔ), 讓其滿(mǎn)8位。

           11000 110011
                 111111         (code & 0b111111)
           ------------------
                 110011  
              10 000000         (code & 0b111111) | 0b10000000
          -------------------
              10 110011

          Base64編碼和解碼

          utf-8轉(zhuǎn)Base64編碼規(guī)則

          1. 獲取每個(gè)字符的Unicode碼,轉(zhuǎn)為utf-8編碼
          2. 三個(gè)字節(jié)作為一組,一共是24個(gè)二進(jìn)制位
            字節(jié)數(shù)不能被 3 整除,用0字節(jié)值在末尾補(bǔ)足
          3. 按照6個(gè)比特位一組分組,前兩位補(bǔ)0,湊齊8位
          4. 計(jì)算每個(gè)分組的數(shù)值
          5. 以第4步的值作為索引,去ASCII碼表找對(duì)應(yīng)的值
          6. 替換第2添加字節(jié)數(shù)個(gè)數(shù)的 =
            比如第2添加了2個(gè)字節(jié),后面是2個(gè)=

          以大掘A為例, 我們通過(guò)上面的utf8_to_binary方法得到utf8的編碼
          11100110 10001110 10011000 11000001, 其字節(jié)數(shù)不能被3整除,后面填補(bǔ)

          11100110
          10001110
          10011000
          01000001
          --------
          00000000
          00000000

          6位一組分為四組, 高位補(bǔ)0, 用| 分割一下填補(bǔ)的。

          00 | 111001  => 57 => 5
          00 | 101000  => 40 => o
          00 | 111010  => 58 => 6
          00 | 011000  => 24 => Y

          00 | 110000  => 16 => Q
          00 | 010000  => 16 => Q
          00 | 000000  =>    => =
          00 | 000000  =>    => =

          結(jié)果是:5o6YQQ==, 完美。

          utf-8轉(zhuǎn)Base64編碼規(guī)則代碼實(shí)現(xiàn)

          基于上面的to_binary方法和base64的轉(zhuǎn)換規(guī)則,就很簡(jiǎn)單啦:
          先看看執(zhí)行效果,very good, 和 base64.us[19] 結(jié)果完全一致。

          console.log(utf8_to_base64("a")); // YQ==

          console.log(utf8_to_base64("?"));  // yII=

          console.log(utf8_to_base64("中國(guó)人")); // 5Lit5Zu95Lq6

          console.log(utf8_to_base64("Coding Writing 好文召集令|后端、大前端雙賽道投稿,2萬(wàn)元獎(jiǎng)池等你挑戰(zhàn)!"));
          //Q29kaW5nIFdyaXRpbmcg5aW95paH5Y+s6ZuG5Luk772c5ZCO56uv44CB5aSn5YmN56uv5Y+M6LWb6YGT5oqV56i/77yMMuS4h+WFg+WlluaxoOetieS9oOaMkeaImO+8gQ==


          完整代碼如下:


          const BASE64_CHARTS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
          function utf8_to_base64(str: string) {
            let binaryStr = to_binary(str);
            const len = binaryStr.length;

            // 需要填補(bǔ)的=的數(shù)量
            let paddingCharLen = len % 24 !== 0 ? (24 - len % 24) / 8 : 0;

            //6個(gè)一組
            const groups = [];
            for (let i = 0; i < binaryStr.length; i += 6) {
              let g = binaryStr.slice(i, i + 6);
              if (g.length < 6) {
                g = g.padEnd(6, "0");
              }
              groups.push(g);
            }

            // 求值
            let base64Str = groups.reduce((b64str, cur) => {
              b64str += BASE64_CHARTS[+`0b${cur}`]
              return b64str
            }, "");

            // 填充=
            if (paddingCharLen > 0) {
              base64Str += paddingCharLen > 1 ? "==" : "=";
            }

            return base64Str;
          }


          至于解碼,是其逆過(guò)程,留給大家去實(shí)現(xiàn)吧。

          其他的成熟方案

          1. 當(dāng)然是基于已有的 btoaatob,

          但是 unescape是不被推薦使用的方法

          function utf8_to_b64( str ) {
            return window.btoa(unescape(encodeURIComponent( str )));
          }

          function b64_to_utf8( str ) {
            return decodeURIComponent(escape(window.atob( str )));
          }

          // Usage: utf8_to_b64('? à la mode'); // "4pyTIMOgIGxhIG1vZGU=" b64_to_utf8('4pyTIMOgIGxhIG1vZGU='); // "? à la mode"

          1. MDN的 rewriting `atob()` and `btoa()` using `TypedArray`s and UTF-8[20]

          其支持到6字節(jié),但是可讀性并不好。

          1. 第三方庫(kù) base64-js[21] 與 js-base64[22]都是周下載量過(guò)百萬(wàn)的庫(kù)。

          雖然有那么多成熟的,但是我們理解和自己實(shí)現(xiàn),才能更明白Base64的編碼原理。

          額外補(bǔ)充一點(diǎn)

          1. 編碼關(guān)系圖

          借用[你真的了解 Unicode 和 UTF-8 嗎?[23]]一張圖:

          image.png
          1. DOMString[24] 是utf-16編碼

          引用

          Version-Specific Charts[25]
          Unicode13.0.0[26]
          Unicode? 13.0 Versioned Charts Index[27]
          RFC 4648 | The Base16, Base32, and Base64 Data Encodings[28]
          Base64 encoding and decoding[29]
          字符編碼筆記:ASCII,Unicode 和 UTF-8[30]
          Unicode與JavaScript詳解[31]
          Base64 編碼入門(mén)教程[32] Base64原理[33]
          詳解base64原理[34]
          一文讀懂base64編碼[35]
          JS 中關(guān)于 base64 的一些事[36]
          Base64 的原理、實(shí)現(xiàn)及應(yīng)用[37]
          圖片與Base64換算關(guān)系[38]
          [你真的了解 Unicode 和 UTF-8 嗎?[39]]
          Unicode中UTF-8與UTF-16編碼詳解[40]
          Unicode對(duì)應(yīng)表[41]
          JavaScript Source Map 詳解[42]

          ?? 看完三件事

          如果你覺(jué)得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:
          • 點(diǎn)個(gè)【在看】,或者分享轉(zhuǎn)發(fā),讓更多的人也能看到這篇內(nèi)容
          • 關(guān)注公眾號(hào)【趣談前端】,定期分享 工程化 可視化 / 低代碼 / 優(yōu)秀開(kāi)源。




          從零搭建全??梢暬笃林谱髌脚_(tái)V6.Dooring

          從零設(shè)計(jì)可視化大屏搭建引擎

          Dooring可視化搭建平臺(tái)數(shù)據(jù)源設(shè)計(jì)剖析

          可視化搭建的一些思考和實(shí)踐

          基于Koa + React + TS從零開(kāi)發(fā)全棧文檔編輯器



          點(diǎn)個(gè)在看你最好看


          瀏覽 79
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人精品秘 久久久按摩下载 | 色婷婷手机在线视频 | 亚洲免费在线视屏 | 超碰中文在线字幕 | 爱情岛论坛av |