Golang で外部ファイルをコンパイル/ビルドしたバイナリに埋め込みたい。
例えば JSON ファイルとか、画像とか。多言語の
bind
みたいな感じで。io/ioutil
も微妙に使い勝手が悪いし、今後使えなくなる(deprecated
になった)らしいし。知らんけど。
かと言って、myData := []byte(`{"hoge": "fuga"}`)
のようにソースコードに埋め込みたくないのです。改行とかメンテが面倒なのです。画像なんかも Base64 エンコードとかでハードコーディングしたくないのです。
「golang ファイル 埋め込み サンプル」で Qiita 記事をググっても、ドンピシャのサンプルがなかったので、自分のググラビリティとして。
TL; DR (今北産業)
- Golang 1.16 以上で
embed
と言う、まんまのパッケージが使えるようになった。 - 埋め込みたいデータの相対パスを、変数定義の直前にコメントで指定する。
(//go:embed <データのパス>
) - 複数ファイルを一気に読み込む時は
embed.FS
型に入れて、hoge.ReadFile("<データのパス>")
メソッドで取り出す。
TS; DR (マスター、とりあえず動くものをくれ)
1 つのファイルを埋め込むサンプル
package main
import (
_ "embed" // 下記 //go:embed を機能させるために _ で
// 読み込みだけさせておく。ここ重要。
"encoding/json"
"fmt"
)
// データの埋め込み(詳しくは「同梱ファイルのパスについて」参照)
//go:embed data/sample.json
var myRawData []byte
// JSON データの構造を定義
type TData struct {
Name string `json:"name"`
}
func main() {
// 埋め込まれたデータから JSON としてパース(Unmarshal)する
var d TData
json.Unmarshal(myRawData, &d)
// パースされたデータの確認
fmt.Printf("%v\n", d.Name)
}
// Output: hoge
{
"name": "hoge"
}
- オンラインで動作をみる @ Go Playground
- 動作確認バージョン: Go version 1.16 and 1.18.1
【同梱ファイルのパスについて】
//go:embed data/sample.json
var myRawData []byte
上記はパス先(data/sample.json
)のデータを、変数 "myRawData
" に読み込み、バイナリに埋め込みます。埋め込まれたデータは raw(生)データの []byte
型のままです。
また、この時、対象となるファイルは、このソース(main.go
)から見たディレクトリ以下に設置されている必要があります。
"../../hoge/fuga.png
" のように、上位のディレクトリの参照は仕様で参照できません。
go:embed only allowed in Go files that import "embed"
エラー
上記エラーが出る場合、import
で embed パッケージが読み込まれていません。
ソース内で明示的に embed.FS
などのモジュールの関数を使っていない場合、gofmt
などで消されてしまいます。
単体ファイルを単純に埋め込みたい場合は import _ "embed"
と強制インポートする必要があります。
複数のファイルを 1 回で読み込むサンプル(ディレクトリ丸ごとの読み込み含む)
package main
import (
"embed"
"encoding/json"
"fmt"
"runtime"
)
// 複数データの埋め込み。
// パスのデータを同じ構成で"myRawDatas"に読み込む。この時点では raw(生)データのまま。
//
//go:embed all:assets
//go:embed data/sample.json
//go:embed foo.json
//go:embed bar.json
var myRawDatas embed.FS
// JSON データの構造を定義しておく
type TData struct {
Name string `json:"name"`
}
func main() {
// Go バージョンの確認
fmt.Printf("Go version: %v\n", runtime.Version())
// アセットに埋め込まれたディレクトリを参照する
{
myRawData1, _ := myRawDatas.ReadFile("assets/sample1.txt")
fmt.Println("assets/sample1.txt:", string(myRawData1))
myRawData2, _ := myRawDatas.ReadFile("assets/sample2.txt")
fmt.Println("assets/sample2.txt:", string(myRawData2))
}
// 埋め込まれたデータから JSON としてパース(Unmarshal)する
{
d := TData{}
myRawData, _ := myRawDatas.ReadFile("data/sample.json")
json.Unmarshal([]byte(myRawData), &d)
fmt.Printf("sample.json: %v\n", d.Name)
}
{
d := TData{}
myRawData, _ := myRawDatas.ReadFile("foo.json")
json.Unmarshal([]byte(myRawData), &d)
fmt.Printf("foo.json: %v\n", d.Name)
}
{
d := TData{}
myRawData, _ := myRawDatas.ReadFile("bar.json")
json.Unmarshal([]byte(myRawData), &d)
fmt.Printf("bar.json: %v\n", d.Name)
}
// Output:
// assets/sample1.txt: Sample text 1.
//
// assets/sample2.txt: Sample text 2.
//
// Go version: go1.21.4
// sample.json: hoge
// foo.json: foo
// bar.json: bar
}
Sample text 1.
Sample text 2.
{
"name": "hoge"
}
{
"name": "foo"
}
{
"name": "bar"
}
- オンラインで動作をみる @ Go Playground
- 動作確認バージョン: Go version 1.16