在一個 缺少紀律 人多手雜的開發團隊中,常常會有不同部分是由不同人負責的情形。 今天這個狀況就是一個有趣的例子,不過也可以當作一個有趣的經驗…

認識 Base64 編碼

一直以來都以為我已經知道 Base64 編碼是什麼, 可能這一切要怪 Node.js 環境下 Buffer 做的太無腦防呆, 剛好藉這次機會認識一下 Base64 編碼到底在幹嘛。 如果你覺得你已經認識 Base64 的話可以跳過這裡, 要不然建議你看一下 維基百科

Padding 是什麼

在這裡還是幫大家稍微抓個重點。 Base64 是把 3 個 byte 轉成 4 個 64 進位表示的方式。 (2^6 = 64,即每 6 個 bit 為一個 64 進位單元。 LCM(8, 6) = 24,所以就可以得到前面的結論。) 但是當然不可能你要編碼的所有東西長度都會是剛好 3 的倍數, 所以不夠的部分在結尾就用 = 來補上。

Base64轉換方式
從維基截下來的Base64編碼範例

不過除了 padding 之外,因為 padding 的作用只是把不足 3 byte 的部分補齊, 所以就算沒有 padding 其實也不會有任何的差異,所以就有沒有 padding 的版本啦~ (就是結尾沒有 =)

為什麼會混用

這就是個很好的問題了,可能是專案中間經過了很多人,然後交接沒做好, 或者是有人沒看文件就做了, 或者是專案對於這些事情沒有一個統一的規範, 又或者是… 算了,反正就是一些奇怪的原因造成混用了。 然後,Go 對於編碼的要求相對比較嚴謹, 所以就出現了這個奇怪的問題~

Go 裡面的 Base64 編碼

Go 的 Standard Library 其實就已經有對 Base64 處理的 package, 就是 encoding/base64 這個 package。 不過其實他是定義了各種不同的 Base64 encoding, 像是標準的 rfc4648 定義的就是 StdEncoding, 不過還有另外一個沒有 padding 的 (就是不夠不需要補 = 的版本), 叫做 RawStdEncoding。 這兩個 encoding 並不相容,所以當結尾沒有 = 但是又需要 padding 的時候, StdEncoding 就會解不出來,反之亦然。

base64.StdEncoding.DecodeString("QQ==") // ok
base64.StdEncoding.DecodeString("QQ") // illegal base64 data at input byte 0

解決方法

其實解決方式也是蠻明顯的, 就前面的說明我們知道這兩種 encoding 只差在 = 這個結尾的 padding 而已, 所以我們可以把所有的 Base64 都轉成其中一種就可以囉! 例如下面的作法是先把所有的 padding 去掉再進行 decode。

func b64Decode(s string) ([]byte, error) {
	return base64.RawStdEncoding.DecodeString(strings.TrimRight(s, "="))
}