在Web前端還可以這樣實現Base64

相關文章:
一步到位 Base64 編碼
Base64 之 JavaScript 實現

上面的相關文章中有一篇Base64 之 JavaScript 實現,本篇與之雖有異曲同工之妙,但它們是兩種完全不同的實現方式。本篇在講解上更優,因為對基礎知識講的更細致,分析更透徹。

btoa 和 atob

btoa 方法

btoa 是 Binary To ASCII 的簡寫,意思就是把二進制數據編碼轉換成Base64編碼的ASCII字符串。且btoa(str) 方法是瀏覽器中的一個全局(頂級)方法。

atob 方法

btoa 相反 atob 是 ASCII To Binary 的簡寫,意思是把Base64格式的ASCII字符串進行Base64解碼,得到原數據。atob 正是 btoa 方法的逆過程,并且它也是瀏覽器中的一個全局(頂級)方法。

萬事大吉?

使用瀏覽器下原生的 JavaScript 方法(btoaatob)已經完全可以做到對字符串和二進制數據進行Base64的編碼與解碼;看到這里是不是覺得這一篇文章可以收尾了?其實這才剛剛開始!原理是因為給 btoa 傳遞一個中文字符串作為參數時,會出現如下代碼段所示的錯誤。

> btoa("我是仵士杰");
< VM197:1 Uncaught DOMException: Failed to execute 'btoa' on 'Window': 
The string to be encoded contains characters outside of the Latin1 range.(…)

為什么會報錯呢?

The string to be encoded contains characters outside of the Latin1 range.
被編碼的字符串包含Latin1范圍以外的字符。

Latin1 是什么東西?

ISO/IEC8859-1,又稱Latin-1或“西歐語言”,是國際標準化組織內ISO/IEC 8859的第一個8位字符集。以ASCII為基礎,在空置的0xA0-0xFF的范圍內,加入96個字母及符號,藉以供使用變音符號的拉丁字母語言使用。

看明白了吧,其實btoa只能轉換占一個字節寬度的字符,就是Latin1字符集(它是ASCII的超集)。而中文漢字是被編碼成占兩個或以上個字節的。所以btoa方法無法對中文進行操作,于是就報了上面看到的錯誤。

曲線救國第一步

曲線救國的第一步我們先來介紹一下encodeURI、encodeURIComponent、decodeURI 和 decodeURIComponent這四個方法。它們是對字符串進行URI編碼和解碼的,也稱之為轉義。下面分別對他們進行介紹。

encodeURI 方法

下面是引用了w3cshool網站上對 encodeURI 方法的介紹

該方法不會對 ASCII 字母和數字進行編碼,也不會對這些 ASCII 標點符號進行編碼: - _ . ! ~ * ' ( ) 。
該方法的目的是對 URI 進行完整的編碼,因此對以下在 URI 中具有特殊含義的 ASCII 標點符號,encodeURI() 函數是不會進行轉義的:;/?:@&=+$,#

除上述介紹中明確說明不會被轉義的字符外,URI中所有其他字符(如中文字符等)都會被轉義。請看下面的代碼段:

> encodeURI("http://www.ibestcode.com/?name=仵士杰");
< "http://www.ibestcode.com/?name=%E4%BB%B5%E5%A3%AB%E6%9D%B0"

encodeURIComponent 方法

還是引用w3cshool網站上對上的介紹

該方法不會對 ASCII 字母和數字進行編碼,也不會對這些 ASCII 標點符號進行編碼: - _ . ! ~ * ' ( ) 。
其他字符(比如 :;/?:@&=+$,# 這些用于分隔 URI 組件的標點符號),都是由一個或多個十六進制的轉義序列替換的。

除上述介紹中明確說明不會被轉義的字符外,URI中所有其他字符(如中文字符和ASCII的“;/?:@&=+$,# ”等)都會被轉義。請看下面的代碼段:

> encodeURIComponent("http://www.ibestcode.com/?name=仵士杰");
< "http%3A%2F%2Fwww.ibestcode.com%2F%3Fname%3D%E4%BB%B5%E5%A3%AB%E6%9D%B0"

通過比較我們會發現 encodeURIComponent 與 encodeURI 相比,多轉換了在URI中具有特殊含義的字符:“;/?:@&=+$,#”。

decodeURI 和 decodeURIComponent

通過名字我們就能知道 decodeURIencodeURI的逆過程,而 decodeURIComponentencodeURIComponent 的逆過程,在這里就不多做介紹了,請看下面的代碼段;

> decodeURI("http://www.ibestcode.com/?name=%E4%BB%B5%E5%A3%AB%E6%9D%B0");
< "http://www.ibestcode.com/?name=仵士杰"

> decodeURI("http%3A%2F%2Fwww.ibestcode.com%2F%3Fname%3D%E4%BB%B5%E5%A3%AB%E6%9D%B0");
< "http%3A%2F%2Fwww.ibestcode.com%2F%3Fname%3D仵士杰"

> decodeURIComponent("http%3A%2F%2Fwww.ibestcode.com%2F%3Fname%3D%E4%BB%B5%E5%A3%AB%E6%9D%B0");
< "http://www.ibestcode.com/?name=仵士杰"

> decodeURIComponent("http://www.ibestcode.com/?name=%E4%BB%B5%E5%A3%AB%E6%9D%B0");
< "http://www.ibestcode.com/?name=仵士杰"

轉義規則

從上面幾段代碼中大家應該已經發現,字符串“仵士杰”轉義之后對應的是“%E4%BB%B5%E5%A3%AB%E6%9D%B0”;字符“://”轉義后對應的是“%3A%2F%2F”;其實轉義的規則是把字符的utf-8編碼以十六進制顯示,并在每個字節(8bits=2個十六進制位)前加字符‘%’。有興趣的同學可以親自去驗證一下。

小結一下

btoa 方法的參數中不能有中文,而encodeURIencodeURIComponent 都可以對中文進行轉義,并且轉義后的所有字符都是ASCII碼字符。如此用encodeURIencodeURIComponent轉義后的字符串再用btoa函數操作是不是就可以了呢?當然這樣做是不會報錯了,但最終得到的字符串適用性不強,因為這不是單純的Base64編碼,而是先進行encodeURI轉義后再進行Base64編碼的結果。如果要得到原來的字符串,還是需要先進行Base64解碼,再進行decodeURI解碼兩步操作才行的。

柳暗花明 escape 和 unescape

escape 方法

同樣引用W3CSchool上面的介紹

該方法不會對 ASCII 字母和數字進行編碼,也不會對下面這些 ASCII 標點符號進行編碼: * @ - _ + . / 。其他所有的字符都會被轉義序列替換。

escape 方法接受一個字符串(是字符串,不是二進制串),他會根據字符編碼占用的字節數不同而使用不同的方式顯示編碼。看下面的例子:

> escape(":%?")
< "%3A%25%3F"
> escape("仵士杰")
< "%u4EF5%u58EB%u6770"

看出什么門道沒?沒看明白也沒關系,下面我來解釋一下:
其實 escape 的眼里所有字符都是Unicode編碼的, 如果遇到的字符Unicode編碼只占一個字節(其實就是Latin1字符集部分,[在這里說明一下,Unicode 字符集是 Latin1字符集的超集]),就以 "%"+Unicode編碼值的十六進制表示來編碼。如“:”被編碼成“%3A”。如果遇到的字符Unicode編碼占兩個字節,就以"%u"+Unicode編碼的十六進制表示來編碼。如“仵”被編碼成“%u4EF5”,有興趣的同學可以親自去查Unicode編碼表來驗證。關于占三四個字節的情況在此就不在討論了,有興趣的同學可以自己去深挖一下,挖出寶貝記得要跟我分享啊,哈哈……。

unescape 方法

unescape 方法執行的操作正是 escape 方法的逆過程,他會把所有“%XX”(XX是兩位十六進制值)轉換到Unicode中一字節能表示的部分(其實就是Latin1字符集中的字符)。unescape 函數是一個頂級 JavaScript 函數,并不與任何對象關聯。

氣滿放大招

綜合以上所述,我們可以清楚的知道,encodeURI 和 encodeURIComponent 會把漢字等轉換成UTF-8編碼后對每個字節進行轉義得到類似"%XX"(XX是兩位十六進制值)的串。而unescape 可以把所有 "%XX"(XX是兩位十六進制值)的串,解碼到Latin1字符集上。btoa 方法正好能夠操作Latin1字符集上的字符轉換成Base64編碼。于是乎以下代碼段產生了:

function utf8ToBase64(str){
  return btoa(unescape(encodeURIComponent(str)));
}

而解碼過程是編輯過程的逆過程,于是得到如下代碼

function base64ToUtf8(str){
  return decodeURIComponent(escape(atob(str)));
}

如果要像之前發的文章Base64 之 JavaScript 實現寫的那樣,封裝成一個Base64對象,就可以得到如下代碼了:

!function(W){
  W.Base64 = {
     utf8ToBase64:function (str){
       return btoa(unescape(encodeURIComponent(str)));
     },
     base64ToUtf8: function(str){
        return decodeURIComponent(escape(atob(str)));
      }
  }
}(window);

//下面是測試結果

> Base64.utf8ToBase64("仵士杰")
< "5Lu15aOr5p2w"

> Base64.base64ToUtf8("5Lu15aOr5p2w")
< "仵士杰"

打完收功!

相關文章

一步到位 Base64 編碼
Base64 之 JavaScript 實現

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 第5章 引用類型(返回首頁) 本章內容 使用對象 創建并操作數組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,270評論 0 4
  • 前一陣做活動有一個分享文案總是分享錯誤,排除法之后發現是一個字符的編解碼問題。好幾次遇到這種問題都是懵過去的,這次...
    lzxxx閱讀 720評論 1 1
  • 記得寫作這個事情,還是小學五年級的時候,以前學寫作文都像擠牙膏一樣的,小學五年級的時候,新換了一個語文老師,...
    SYbook閱讀 159評論 0 1
  • 上次在馬路邊我看到一個白白的蘑菇,我叫它“白菌菇”,今天我和媽媽上網查詢才知道原來是“雞腿菇”。我在想雞腿...
    rx任曦閱讀 405評論 0 0
  • 最近籌備,好久沒寫簡書了, 來到黃陂籌備第四天,漢陽剛做完籌備就來黃陂籌備。來黃陂的前一天晚上,一直搞到5點,6點...
    每天記錄閱讀 259評論 0 0