go:embedを使ってローカルにあるファイルを呼び出す方法
はじめに
ローカルにあるファイルを呼び出す方法として、go言語を使用して
プロジェクト配下にあるファイルであればファイルパスを指定してos
などを使用して読み込むことができますが
go build
コマンドでビルドした場合には.go
ファイル以外はビルドの対象にならないため
ビルドしたファイルを実行しても、読み込み対象のファイルが存在しないためエラーが発生してしまいます。
go:embedとは
go 1.16から追加されたpackageで
外部ファイルをプログラムの中に埋め込むための機能です。
これにより、プログラムと一緒に配布される必要のある設定ファイルやリソースファイルを、実行バイナリとしてパッケージングすることができます。
プログラム内で指定した、.go
ファイル以外を実行ファイルに組み込むことができるようになるので、デプロイや配布が容易になります。
ファイルを読み込むのではなく、ビルド時にバイナリファイルとして取り込んでいるので
ファイル読み込みのオーバーヘッドもなく気軽に使えそうです。
使い方
環境
go 1.16以降の環境を用意します
パッケージはこちらです1つのファイルを読み込む
import _ "embed"
embed packageをimportします、
1つのファイルをインポートする場合は直接packageを使用しないのでブランクでimportしておきます
読み込み対象のファイル
hello
world
1つのファイルを変数に格納する方法はこちら
あまり見慣れない書き方ですが
//go:embed
のようにGoのディレクティブを使用して、コンパイル時にファイルを読み込んで変数に内容を格納しています
単一のファイルを取り込む場合はstring
または[]byte
を使用できます
//go:embed hello.txt
var hello string
//go:embed file/world.txt
var world []byte
func main() {
fmt.Println(hello)
fmt.Println(world)
}
上記を実行するとファイルの内容が読み取れています
❯❯❯ go run ./main.go
hello
world
ディレクトリ内の複数ファイルを読み込む
import "embed"
読み込み対象のファイル
./file
├── ./hello.txt
└── ./world.txt
読み込みたいファイルを読み込み対象のディレクトリに格納し
//go:embed file/*
var files embed.FS
func main() {
hello, _ := files.ReadFile("file/hello.txt")
world, _ := files.ReadFile("file/world.txt")
fmt.Println(string(hello), string(world))
}
embed.FS型の変数に go:embed
で対象のファイルを指定します
このとき、ワイルドカードを使用してファイルを指定することで複数のファイルやディレクトリを読み込めます。
プログラム内で利用するときは
files.ReadFile("file/world.txt")
のようにReadFileでファイル名を指定して呼び出します,os.File
型の呼び出しと同じくファイルを指定して呼び出す形で呼び出せます。
embed.FS型
embed.FS
型はio/fs
パッケージのFSインターフェイスを実装しているため
net/http
text/template
html/template
等、ファイルシステムを引数として取るpackageから簡単に呼び出すことができます
例 net/httpとの組み合わせ
起動しているhttpサーバのパスに、FS
で指定したファイルを配置するだけで
コンテンツを配信することができます。
os.File
等を使用して静的ファイルを配信する場合だと、goのビルドファイルには含まれないため
ビルドしたファイルとは別に静的ファイルを管理する必要がありましたが、embedでファイルを指定する場合は
ビルドファイル内に内包されるため、管理がとても楽になります。
http.Handle("/static/", http.StripPrefix("/static/",http.FileServer(http.FS(content))))
例 text/templateとの組み合わせ
{{.company}}
お世話になっております。
弊社主催の「{{.seminar}}」にお申し込みいただき
誠にありがとうございました。
func main() {
data := map[string]string{
"company": "株式会社レスキューナウ",
"seminar": "防災セミナー",
}
t, _ := template.New("mail.tmpl").ParseFS(files, "file/mail.tmpl")
if err := t.Execute(os.Stdout, data); err != nil {
log.Fatal(err)
}
}
実行結果
株式会社レスキューナウ
お世話になっております。
弊社主催の「防災セミナー」にお申し込みいただき
誠にありがとうございました。
まとめ
この記事では//go:embed
とembed.FS
型の使い方について記載しました
プログラムとしてはos.File
型のファイル呼び出しとそこまで大きな変化はありませんが、外部リソースをプログラムに埋め込むことで、アプリケーションのデプロイが簡単になるので非常に使いやすいと感じました。
Discussion