ファイルを選択
  1. base64hub
  2. 各言語での Base64
  3. Go (Golang)
コード

Go での Base64

Go の encoding/base64 パッケージには、すぐに使える 4 つのエンコーディングとストリーム用のラッパーがあります。作業の大半は、4 つのうち正しいものを選ぶことです。以下の出力は Go 1.25 のものです。

文字列のエンコードとデコード

Go の文字列はもともと UTF-8 なので、[]byte に変換するだけで済みます。

package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	enc := base64.StdEncoding.EncodeToString([]byte("héllo wörld"))
	fmt.Println(enc) // aMOpbGxvIHfDtnJsZA==

	dec, err := base64.StdEncoding.DecodeString(enc)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(dec)) // héllo wörld
}

err は必ずチェックしてください。失敗時、DecodeString はそこまでにデコードしたバイト列をエラーと一緒に返すことがあるため、チェックせずに結果を使うと、知らないうちに途中で切れたデータを扱うことになります。

4 つのエンコーディング:Std、URL、Raw

変数アルファベットパディング<<???>> のエンコード結果
StdEncoding+ /ありPDw/Pz8+Pg==
URLEncoding- _ありPDw_Pz8-Pg==
RawStdEncoding+ /なしPDw/Pz8+Pg
RawURLEncoding- _なしPDw_Pz8-Pg
d := []byte("<<???>>")
base64.URLEncoding.EncodeToString(d)    // PDw_Pz8-Pg==
base64.RawURLEncoding.EncodeToString(d) // PDw_Pz8-Pg

b, err := base64.RawURLEncoding.DecodeString("PDw_Pz8-Pg") // "<<???>>", nil

デコードは、パディングについてどちらの方向にも厳格です。パディング付きのエンコーディングはパディングのない入力を拒否し、Raw 系は = を拒否します。

base64.StdEncoding.DecodeString("aGk")
// illegal base64 data at input byte 0
base64.RawURLEncoding.DecodeString("PDw_Pz8-Pg==")
// illegal base64 data at input byte 10

複数のソースから入力を受け付けるなら、strings.TrimRight(s, "=") で末尾の = を取り除いてから Raw のエンコーディングでデコードしてください。base64.NewEncoding(alphabet) と .WithPadding(base64.NoPadding) で独自のエンコーディングを作ることもできます。それぞれのバリアントが使われる場面は Base64 と Base64URL の違い で説明しています。

ファイルのエンコードとデコード

data, err := os.ReadFile("hello.txt")
if err != nil {
	log.Fatal(err)
}
fmt.Println(base64.StdEncoding.EncodeToString(data)) // aGVsbG8K

raw, err := base64.StdEncoding.DecodeString("aGVsbG8K")
if err != nil {
	log.Fatal(err)
}
os.WriteFile("copy.txt", raw, 0o644)

NewEncoder と NewDecoder によるストリーム処理

大きなファイルは io.Writer か io.Reader でラップします。メモリ使用量は一定に保たれます。

in, _ := os.Open("big.bin")
defer in.Close()
out, _ := os.Create("big.b64")
defer out.Close()

w := base64.NewEncoder(base64.StdEncoding, out)
if _, err := io.Copy(w, in); err != nil {
	log.Fatal(err)
}
if err := w.Close(); err != nil { // flushes the last partial block
	log.Fatal(err)
}

w.Close() は out を閉じるのではなく、最後の 1〜2 バイトとパディングを書き出します。これを忘れるのが Go の Base64 の典型的なバグで、出力は一見正常なのに最大 4 文字足りなくなります。デコードも対称的です。

r := base64.NewDecoder(base64.StdEncoding, f)
io.Copy(dst, r)

300 KB のテストファイルでは、ストリーミングとメモリ上の処理の結果が同一でした。デコーダーは \r と \n を読み飛ばすので、MIME 形式で折り返された入力を直接読めます。バッファサイズを事前に計算するには、EncodedLen(5) が 8 を、RawStdEncoding.EncodedLen(5) が 7 を返します。その裏にある式は サイズ増加の解説 にあります。

Go 1.22 では、既存のスライスに追記する AppendEncode と AppendDecode が追加されました。ヘッダーを作るときに便利です。

dst := base64.StdEncoding.AppendEncode([]byte("Basic "), []byte("user:pass"))
fmt.Println(string(dst)) // Basic dXNlcjpwYXNz

Basic Auth ヘッダージェネレーター なら、同じ値をブラウザで作れます。

検証と CorruptInputError

デコードのエラーはすべて base64.CorruptInputError で、最初の不正なバイトのオフセットを表す int64 です。

_, err := base64.StdEncoding.DecodeString("PDw_Pz8-Pg==")
var ce base64.CorruptInputError
if errors.As(err, &ce) {
	fmt.Println(err) // illegal base64 data at input byte 3
}

StdEncoding での結果:

入力エラー
PDw_Pz8-Pg==illegal base64 data at input byte 3(_ の位置)
aGkillegal base64 data at input byte 0
aGk=!!illegal base64 data at input byte 4
aGk==illegal base64 data at input byte 4
aGVs bG8Killegal base64 data at input byte 4(スペース)
aGl=エラーなし、hi を返す
"aGVs\nbG8K"エラーなし、改行は読み飛ばす

aGl= はパディングビットが 0 ではありません。このような正規形でない入力を拒否するには .Strict() を使います。

_, err = base64.StdEncoding.Strict().DecodeString("aGl=")
// illegal base64 data at input byte 3

Base64 バリデーター も同じ位置を強調表示します。よくある原因は よくある Base64 のエラー にまとめています。

落とし穴

  • JWT のパートを StdEncoding でデコードする。 トークンはパディングなしの Base64URL です。RawURLEncoding を使うか、まず JWT デコーダー でトークンを確認してください。
  • 途中までの結果を見落とす。 "aGk=!!" は "hi" とエラーの両方を返します。err != nil のときはバイト列を使わないでください。
  • エンコーダーの Close() を忘れる。 最後のブロックが失われます。
  • コピー&ペーストで紛れ込んだスペース。 デコーダーが読み飛ばすのは \r と \n だけです。それ以外の空白は、先に strings.Join(strings.Fields(s), "") で取り除いてください。

バージョンごとの注意

  • RawStdEncoding と RawURLEncoding:Go 1.5
  • Strict():Go 1.8
  • AppendEncode、AppendDecode:Go 1.22
  • このパッケージには MIME の折り返しを行うエンコーダーはありません。メールは mime と net/mail パッケージで扱うか、76 文字ごとに自分で \r\n を挿入してください。

よくある質問

URLEncoding と RawURLEncoding の違いは何ですか?

どちらも - と _ を使う URLセーフなアルファベットです。URLEncoding は = のパディングを付け、デコード時にも要求します。RawURLEncoding はパディングを省略し、付いていると拒否します。JWT は RawURLEncoding を使います。

「illegal base64 data at input byte 0」とはどういう意味ですか?

base64.CorruptInputError で、数値はデコードに失敗したオフセットです。短い文字列でバイト 0 と出る場合、たいていは長さの誤り、多くはパディングの不足が原因です。パディングのない入力には Raw のエンコーディングを使ってください。

Go の Base64 デコーダーは改行を無視しますか?

はい。4 つのエンコーディングすべてが \r と \n を読み飛ばすので、MIME 形式で折り返された入力もデコードできます。スペースやその他の文字はエラーになります。

続けて読む