go-embedxgo embed 的功能扩展
go1.16 //go:embed 的功能扩展。
简介:
go1.16 官方推出了go:embed 这个非常有意思的功能,可以嵌入静态文件到一个可执行文件中,发布软件时,只需要发布软件的可执行程序就可以了,不需要另外发布那些静态文件。之前这部分功能需要三方脚手架处理,比如go-bindata,go1.16开始,直接在go的工具链中提供了go:embed支持完成相同的工作,使用体验非常的nice。
具体使用示例可以参考tl-explorer:
%> git clone --recursive https://github.com/alimy/tl-explorer
%> cd tl-explorer && pwd
go/src/github.com/alimy/tl-explorer
%> tree -L 2
.
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── assets
│ ├── assets.go
│ └── schema.tl
├── go.mod
├── go.sum
├── main.go
└── scripts
├── launchd
└── systemd
该项目需要的静态文件存放在assets/schema.tl 目录中,需要把这个目录的所有文件都嵌入到最终的可执行程序中,可以这样做(assets/assets.go):
// Copyright 2020 Michael Li <alimy@gility.net>. All rights reserved.
// Use of this source code is governed by Apache License 2.0 that
// can be found in the LICENSE file.
package assets
import (
"embed"
"net/http"
"github.com/alimy/embedx"
)
// NewFileSystem get an assets http.FileSystem instance
func NewFileSystem() http.FileSystem {
//go:embed schema.tl
var content embed.FS
embedFS := embedx.ChangeRoot(content, "schema.tl")
return http.FS(embedFS)
}
声明需要嵌入的那些文件,然后在程序中就可以通过embed.FS访问这些文件,如例子中:
//go:embed schema.tl
var content embed.FS
通过embed.FS访问schema.tl中的文件。
我们这里通过http server访问schema.tl目录中的静态文件(main.go):
// Copyright 2020 Michael Li <alimy@gility.net>. All rights reserved.
// Use of this source code is governed by Apache License 2.0 that
// can be found in the LICENSE file.
package main
import (
"flag"
"fmt"
"log"
"net/http"
"github.com/alimy/tl-explorer/assets"
)
var (
host string
port uint
showVersion bool
version = "v0.2.0"
)
func init() {
flag.StringVar(&host, "host", "", "listening host")
flag.UintVar(&port, "port", 8080, "listening port")
flag.BoolVar(&showVersion, "v", false, "show version")
}
func main() {
flag.Parse()
if showVersion {
fmt.Println(version)
return
}
addr := fmt.Sprintf("%s:%d", host, port)
if host == "" {
host = "localhost"
}
fmt.Printf("listening in [%s]. Please open http://%s:%d in browser to enjoy yourself.\n", addr, host, port)
http.Handle("/", http.FileServer(assets.NewFileSystem()))
if err := http.ListenAndServe(addr, nil); err != nil {
log.Fatal(err)
}
}
官方的go:embed使用体验非常好,那有没有其他限制呢?有的,比如上面例子,我们嵌入了schema.tl目录,那么通过embed.FS 访问schema.tl目录下的文件比如index.html时,需要这样访问:
//go:embed schema.tl
var content embed.FS
indexContent, _ := content.ReadFile("schema.tl/index.html")
我其实是想把schema.tl目录作为根目录,访问目录下的文件时,就如当前就在根目录下访问文件,不需要加schema.tl目录名前缀:
//go:embed schema.tl
var content embed.FS
// 预想schema.tl为根目录, 访问shema.tl/index.html, 只给定文件名index.html即可
// indexContent, _ := content.ReadFile("index.html")
go-embedx提供这样的辅助功能特性,这里可以使用辅助库的 ‘改变root目录’ 功能,使用如下:
//go:embed schema.tl
var content embed.FS
// 改变根目录为shema.tl目录
embedFS := embedx.ChangeRoot(content, "schema.tl")
// 访问schema.tl目录下的index.html,可以这样做
indexContent, _ := embedFS.ReadFile("index.html")
fmt.Println(indexContent)
go-embedx还提供其他辅助功能,比如为embed.FS 附加一个虚拟根目录等。
使用实例:
%> cd demo # change to your golang project root directory; cd <your-project-dir>
%> go get github.com/alimy/embedx
%> tree
|- public
|- ...
|- index.html
|- ...
|- conf
|- app.ini
|- ...
|- conf.go
|- ...
|- main.go
|- go.mod
|- ...
%> cat conf/conf.go
// file: conf/conf.go
package conf
import (
"embed"
"github.com/alimy/embedx"
)
func NewConfigFS() embedx.EmbedFS {
//go:embed app.ini
var content embed.FS
// attach a root to conf dir then access files in this returned FS will
// need add 'conf' prefix. eg: access app.ini need FS.ReadFile("conf/app.ini").
return embedx.AttachRoot(content, "conf")
}
%> cat main.go
// file: main.go
package main
import (
"embed"
"github.com/alimy/embedx"
)
func newPublicFS() embedx.EmbedFS {
//go:embed public
var content embed.FS
// change the root to public dir then access files in this returned FS will
// not need 'public' prefix. eg: access public/index.html just need FS.ReadFile("index.html").
return embedx.ChangeRoot(content, "public")
}
```
评论