This is a creation in Article, where the information may have evolved or changed.
When you use Go to develop an app, you sometimes encounter situations where you need to read static resources. For example, to develop WEB applications, the program needs to load the HTML of the template file generation output. In the program deployment, in addition to publishing the application executable file, you also need to publish the dependent static resource files. This adds some trouble to the publishing process. Since publishing a single executable is a very simple operation, someone will find a way to package the static resource file into the Go program file. Here's a look at some of the solutions:
Go-bindata
Go-bindata is currently a tool for pugo embedding static resources in my program. It can embed static files in a go file and provide some ways to do it.
Installation go-bindata :
go get -u github.com/jteeuwen/go-bindata/...
note the last three points ... of the go get address . This parses all subdirectories and downloads dependent compilation subdirectory content. go-bindatathe command tool is in a subdirectory. (also remember to $GOPATH/bin add the system PATH ).
Using the command tool go-bindata (pugo example):
-oOutput file to app/asset/asset.go , package name -pkg=asset , then the directory to be packaged, three points including all subdirectories. This allows all relevant files to be packaged asset.go package asset and initially maintained and directory consistent.
The code for releasing the static file in the Pugo:
dirs := []string{"source", "theme", "doc"} // 设置需要释放的目录for _, dir := range dirs { // 解压dir目录到当前目录 if err := asset.RestoreAssets("./", dir); err != nil { isSuccess = false break }}if !isSuccess { for _, dir := range dirs { os.RemoveAll(filepath.Join("./", dir)) }}
asset.goThe static content inside is still indexed according to the actual directory location. So we can go directly to the directory or file address to operate.
-debug Development Model
go-bindataSupport development mode, that is, do not embed static content, only generate action methods to the output go code, such as:
-debugParameters to open the development mode. The generated code directly reads the static file into memory, rather than coding it into the code. Code files are smaller and you write business logic more quickly.
// -pkg=asset, 打包的包名是 assetbytes, err := asset.Asset("theme/default/post.html") // 根据地址获取对应内容if err != nil { fmt.Println(err) return}t, err := template.New("tpl").Parse(string(bytes)) // 比如用于模板处理fmt.Println(t, err)
http. FileSystem
http.FileSystemIs the interface that defines the HTTP static file service. go-bindatathird-party package GO-BINDATA-ASSETFS implements this interface, which supports HTTP access to the static file directory behavior. Take the example that we compiled above asset.go :
import ( "net/http" "github.com/elazarl/go-bindata-assetfs" "github.com/go-xiaohei/pugo/app/asset" // 用 pugo 的asset.go进行测试)func main() { fs := assetfs.AssetFS{ Asset: asset.Asset, AssetDir: asset.AssetDir, AssetInfo: asset.AssetInfo, } http.Handle("/", http.FileServer(&fs)) http.ListenAndServe(":12345", nil)}
Access http://localhost:12345 , you can see the embedded source , theme doc the Directory list page, and Nginx view static file directory the same.
Go.rice
Go.rice also supports packaging of static files into go files, but behaves go-bindata differently. From the perspective of use, go.rice is actually more convenient static file operation library. Wrapping a static file is a passing feature.
Install and go-bindata like, note three points :
go get github.com/GeertJohan/go.rice/...
go.riceConsider a directory to be an rice.Box operation:
import ( "fmt" "html/template" "github.com/GeertJohan/go.rice")func main() { // 这里写相对于的执行文件的地址 box, err := rice.FindBox("theme/default") if err != nil { println(err.Error()) return } // 从目录 Box 读取文件 str, err := box.String("post.html") if err != nil { println(err.Error()) return } t, err := template.New("tpl").Parse(str) fmt.Println(t, err)}
Rice command
go.riceThe packing command is rice . Very straightforward to use: in the Go Code directory with the Go.rice operation , execute directly rice embed-go :
rice embed-gorice -i "github.com/fuxiaohei/xyz" embed-go // -i 处理指定包里的 go.rice 操作
He will generate the code that embeds the file under the current package name rice-box.go . However, it does not handle import recursively. He will analyze the use of the Go code in the current directory go.rice and find the corresponding folder to embed. But the inside of the sub-directory and import the go.rice use will not be analyzed, you need to manually CD the past or -i specify the package to process the execution command. This is very unfriendly.
http. FileSystem
go.riceis the direct support http.FileSystem interface:
func main() { // MustFindBox 出错直接 panic http.Handle("/", http.FileServer(rice.MustFindBox("theme").HTTPBox())) http.ListenAndServe(":12345", nil)}
A little bit cumbersome is that rice.FindBox(dir) only one directory can be loaded. So a scenario that requires multiple catalogs will have code:
func main() { http.Handle("/img", http.FileServer(rice.MustFindBox("static/img").HTTPBox())) http.Handle("/css", http.FileServer(rice.MustFindBox("static/css").HTTPBox())) http.Handle("/js", http.FileServer(rice.MustFindBox("static/js").HTTPBox())) http.ListenAndServe(":12345", nil)}
Esc
The ESC author, after studying several tools that embed static resources, finds that it's not good enough to write it himself esc . Its requirements are simple, embedded in static resources and support http.FileSystem . esctool also these two main functions.
Installation esc :
go get github.com/mjibson/esc
Use methods and go-bindata similar:
// 注意 esc 不支持 source/... 三个点表示所有子目录go-bindata -o=asset/asset.go -pkg=asset source theme doc/source doc/theme
Direct support http.FileSystem :
import ( "net/http" "asset" // esc 生成 asset/asset.go )func main() { fmt.Println(asset.FSString(false, "/theme/default/post.html")) // 读取单个文件 http.ListenAndServe(":12345", http.FileServer(asset.FS(false))) // 支持 http.FileSystem,但是没有做展示目录的支持}
escThere is a bigger problem is only one file operation, no folder operation, no similar go-bindata asset.RestoreDir() method. And there is no way to list embedded files, resulting in a file operation that cannot be done unless you write yourself dead. That's the biggest reason I don't use him.
Go generate
Tools that embed static resources are recommended for use with go generate . For example pugo , a portal file would have:
package mainimport ( "os" "time" "github.com/go-xiaohei/pugo/app/command" "github.com/go-xiaohei/pugo/app/vars" "github.com/urfave/cli")//go:generate go-bindata -o=app/asset/asset.go -pkg=asset source/... theme/... doc/source/... doc/theme/...// ......
Execute at compile time:
go generate && go build
This is go generate the basic usage. More detailed information can be seen in the official blog.
Summarize
I pugo tested this procedure for embedding static resources when I was developing it. go.riceIt's not the pattern I want, I don't think about it. There esc are too few operating methods available to meet the needs of program development. Last Choice go-bindata . However go-bindata go.rice , both the pure character data or the []byte character data are written to the go file, and the content is very large. escis the BASE64 encoding that is written to the gzip compression stream. The size of the go code is significantly smaller after compression (I embed text files such as templates). You can see that library classes have their own pros and cons. It would be nice to have that go-bindata rich API and esc embed compressed character data like that.