Go 言語はシングルバイナリをウリにしたプログラミング言語です。バイナリファイルを1つポンと scp で転送すれば動くのでとても便利です。シングルバイナリとなると当然、画像や HTML といったアセットをバイナリに埋め込みたくなります。
Go 言語ではこれまで go-assets や go-bindata、statik というツールを使う事でファイルのコンテンツをバイナリ化し、変数からアクセスする様にしてきました。
しかしそれらには色々な流儀や OS 間でのまばらな動作など、ユーザにとって納得のいかない物がありました。昨日、Go 言語ではオフィシャルとしてこのファイル埋め込みをサポートする様になりました。Go 1.16 から使える様になります。
cmd/go: add //go:embed support · golang/go@25d28ec · GitHub
+3 −3 src/cmd/go/internal/fsys/fsys.go +1 −1 src/cmd/go/internal/fsys/fsys_test.go +2 −0 src/cmd/go/...
https://github.com/golang/go/commit/25d28ec55aded46e0be9c2298f24287d296a9e47
package main
import (
_ "embed"
"net/http"
"github.com/labstack/echo"
)
//go:embed static/logo.png
var contents []byte
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.Blob(http.StatusOK, "image/png", contents)
})
e.Logger.Fatal(e.Start(":8989"))
}
変数に //go:embed [ファイル名]
というコメントを付ける事でそのコンテンツを変数に埋め込む事ができます。開発者がやるのは embed
をブランクインポートする事と go build
だけです。ファイルが読み込めなかったり、記法が不正な場合はコンパイルエラーとなります。
pattern static/logo1.png: no matching files found
またバイト列ではなく文字列でも使えます。
package main
import (
_ "embed"
"fmt"
)
//go:embed message.txt
var message string
func main() {
fmt.Println(message)
}
これまで go generate
を使ってバイナリファイルを変数に埋め込んでいたので、それから比べるとずいぶん便利になりました。そしてファイルだけでなく、ファイルシステムとして埋め込む事もできる様になりました。
package main
import (
"embed"
"io"
"log"
"os"
"path"
)
//go:embed static
var local embed.FS
func main() {
fis, err := local.ReadDir("static")
if err != nil {
log.Fatal(err)
}
for _, fi := range fis {
in, err := local.Open(path.Join("static", fi.Name()))
if err != nil {
log.Fatal(err)
}
out, err := os.Create("embed-" + path.Base(fi.Name()))
if err != nil {
log.Fatal(err)
}
io.Copy(out, in)
out.Close()
in.Close()
log.Println("exported", "embed-"+path.Base(fi.Name()))
}
}
embed.FS
は実際にファイルが開ける Open
メソッドをサポートしている為、 http.FileSystem
が返す様なファイルシステムとは互換性がなく、以下の様にウェブサーバに使う事はできないですが、いずれサードパーティからラップするライブラリが登場すると思います。
package main
import (
"embed"
"net/http"
"github.com/labstack/echo"
)
//go:embed static
var local embed.FS
func main() {
e := echo.New()
e.GET("/", echo.WrapHandler(http.FileServer(local)))
e.Logger.Fatal(e.Start(":8989"))
}
追記 http.FS
という関数が既に用意されていました。以下のリポジトリの example4 にサンプルを足しています。
Go 1.16 が待ち遠しいですね。
Go 1.16 が待ち遠しいですね。
※大事な事なので2回言いました。
これらのサンプルは以下のリポジトリに置いてあります。
GitHub - mattn/go-embed-example
We use optional third-party analytics cookies to understand how you use GitHub.com so we can build b...
https://github.com/mattn/go-embed-example