Go v1.16に追加されたembedを試してみる
Go v1.16がリリースされましたね!
追加された機能がいろいろありますが、今回はembedを試してみます!
embedって何?
- embedとは埋め込みという意味
- ファイルの埋め込みができるようになった
- 今までは、ファイルを読み込みは
os
やio/ioutil
で行っていた-
io/ioutil
はv1.16からdeprecatedになった
-
embed何がいいの?
- ファイルの読み込みが楽になった
- ビルド時に埋め込んだファイルはバイナリに含まれる
- Dockerfileとか書くの楽になった
使い方
この記事のサンプルコードはこちらにあります
単一のファイルを読み込む①
sample_bytes.txt
Sample Bytes!
sample_string.txt
Sample String!
main.go
package main
import (
_ "embed"
"fmt"
)
//go:embed sample_bytes.txt
var sampleBytes []byte
//go:embed sample_string.txt
var sampleString string
func main() {
fmt.Printf("%s\n", sampleBytes)
fmt.Printf("%s\n", sampleString)
}
こんな感じでファイルの埋め込みができます。
ディレクティブで埋め込める変数の型は、string、[]byte、embed.FSの3種類です。
埋め込みは関数の内部など閉じたスコープで行うことができません。
単一のファイルを読み込む②
package main
import (
"embed"
"fmt"
"io/fs"
)
//go:embed sample_bytes.txt sample_string.txt
var static embed.FS
func main() {
b, err := static.ReadFile("sample_bytes.txt")
if err != nil {
panic(err)
}
fmt.Printf("%s\n", string(b))
b2, err := fs.ReadFile(static, "sample_string.txt")
if err != nil {
panic(err)
}
fmt.Printf("%s\n", string(b2))
}
このように embed
パッケージを使ってファイルを読み込むことも可能です。
モックサーバー立てる
todo.json
[
{
"id": "a",
"content": "abc"
},
{
"id": "b",
"content": "edf"
}
]
user.json
{
"id": "abc123",
"user": {
"name": "taro",
"age": 20
}
}
main.go
package main
import (
"embed"
"log"
"net/http"
)
//go:embed todo.json user.json
var responseJson embed.FS
func main() {
http.Handle("/", http.FileServer(http.FS(responseJson)))
log.Fatal(http.ListenAndServe(":8080", nil))
}
net/httpパッケージで提供されているファイルシステムとも互換性があります。
http://localhost:8080/user.json
にアクセスするとjsonを取得できます。
ディレクトリごと読み込む
sql/sample1.sql
SELECT *
FROM users
sql/sample2.sql
SELECT *
FROM todos
main.go
package main
import (
"embed"
"fmt"
)
//go:embed sql/*.sql
var sql embed.FS
func main() {
sample1, err := sql.ReadFile("sql/sample1.sql")
if err != nil {
panic(err)
}
fmt.Printf("%s\n", string(sample1))
sample2, err := sql.ReadFile("sql/sample2.sql")
if err != nil {
panic(err)
}
fmt.Printf("%s\n", string(sample2))
}
ワイルドカードを使ってディレクトリごと読み込むことも可能です。
また、../のように親ファイルまで遡って読み込みはできません。
ウェブサイト
ディレクトリ構成
static
L public
L favicon.ico
L index.css
L index.html
main.go
package main
import (
"embed"
"io/fs"
"log"
"net/http"
)
//go:embed static/*
var static embed.FS
func main() {
public, err := fs.Sub(static, "static/public")
if err != nil {
panic(err)
}
http.Handle("/", http.FileServer(http.FS(public)))
log.Fatal(http.ListenAndServe(":8080", nil))
}
こんな感じでワイルドカードを使って階層を掘り下げる形でも使えます。
http://localhost:8080/
にアクセスするとWEBページがちゃんと表示されます。
ビルドするとどうなる?
ビルドするとファイルは埋め込まれます。
つまり、今までのように実行用のバイナリとアセットファイルに別れることはなくなります。
Dockerfileを書いたり、デプロイしたりするのがちょっと楽になりますね!
まとめ
- ファイルの読み込みが楽になった
- ビルド時に埋め込んだファイルはバイナリに含まれる
- Dockerfileとか書くの楽になった
僕が使うなら
僕が今いる現場でクエリを.sql形式のファイルで書かないと嫌だという方がいて、sqlファイルがたくさんあり、それを ioutilで読み込む実装にしているところがあります。
その部分をioutilはdeprecatedになりましたし、embedのが楽にかけますし、Dockerfileもシンプルになるので、そこを置き換えたと思います!
参考にした記事
Discussion