Golang 使用embed打包静态资源文件
在使用golang
撰写一些需要模板技术或使用css
、javascript
、 html
等文件的程序时。这篇文章将告诉你如何使用embed
库将这些静态文件嵌入到二进制文件中。
使用
embed
你的golang
版本必须是v1.6.0及以上.
如何使用
embed
是通过注释指令的方式来告诉编译器那些包文件或子目录文件需要被嵌入在二进制中。
import _ "embed"
//go:embed hello.txt
var s string
print(s)
上面的//go:embed
就是它的指令, 你只能在string
、[]byte
或 FS
类型的变量上使用embed
指令。在go build
时编译器会把hello.txt
内容直接附给变量s
。
http静态服务
在使用http
服务时需要处理很多静态文件,下面将使用embed
来处理这些静态文件。
下面是程序的目录结构,statis
目录是静态资源目录,templates
是模板目录,main.go
是主程序。
- static
- js
- bootstrap.min.js
- css
- bootstrap.min.css
- templates
- layout.html
- dashboard.html
- go.mod
- main.go
- 实现最简单的
http
服务
package main
import (
"net/http"
"os")
func main() {
mux := http.NewServeMux()
err := http.ListenAndServe("127.0.0.1:8080", mux)
if err != nil {
os.Exit(255)
return
}
}
这个http
服务没有任何的路由监听操作。将/static
作为静态文件的路由前缀.比如访问这个: /static/js/bootstrap.min.js
就需要指向对应的目录。
- 添加静态服务
以传统的方工http.Dir
来作来静态服务的文件系统目录器。
fileServer := http.FileServer(http.Dir("/Users/meshell/Projects/go/lwfz-sign"))
mux.Handle("/static/", fileServer)
运行测试下效果: go run main.go
然而通过这种方式绑定的文件服务器,你需要把静态文件目录也打包一份放在对应的指定目录比如上面的/Users/meshell/Projects/go/lwfz-sign
。
- 使用
embed
的FS
对象实现文件服务
上面说过embed
可以对embed.FS
实现嵌入。
var (
//go:embed all:static
assets embed.FS
)
上面的代码指令//go:embed all:static
是告诉编译器嵌入static
目录的所有文件.
- 嵌入指定文件
//go:embed static/js/test.js
- 嵌入所有JS文件
//go:embed static/js/*.js
- 嵌入不同的目录文件
//go:embed static/css/*.css static/js/*.js
定义的变量只是告诉编译器嵌入文件,该如何使用变量实现文件服务器呢。
var (
//go:embed all:static
assets embed.FS
)
// 为静态资源目录生成一前缀目录`static`
func Assets() (fs.FS, error) {
return fs.Sub(assets, "static")
}
func main() {
mux := http.NewServeMux()
// 更改此处的实现
sfs, err := Assets()
if err != nil {
panic(err)
}
fileServer := http.FileServer(http.FS(sfs))
// Assets为静态目录生成前缀目录,所以这里需要加上前缀
// 如果你不想加入前缀你可以直接使用http.FileServer(http.FS(assets))
mux.Handle("/static/", http.StripPrefix("/static", fileServer))
err = http.ListenAndServe("127.0.0.1:8080", mux)
if err != nil {
os.Exit(255)
return
}
}
运行之后的效果和上面的方式是一样的效果,但是静态资源已经打包在二进制文件中,但打包之后的包是比之前的大。这也取决于你的资源文件的大小。
- 模板文件嵌入渲染
监听一个首页/
的路由绑定在dashboard
函数上,渲染模板文件中的dashboard.html
文件。
mux.HandleFunc("/", dashboard)
func dashboard(writer http.ResponseWriter, request *http.Request) {
err := tpl.ExecuteTemplate(writer, "layout", nil)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
}
定义嵌入的模板文件的变量和加载模板的函数.
var (
//go:embed all:templates
templates embed.FS
tpl *template.Template
)
func loadTemplate() {
var err error
tpl, err = template.ParseFS(templates, "templates/layout.html", "templates/dashboard.html")
if err != nil {
panic(err)
}
}
运行之后的效果: go run main.go
完整代码
以下是main.go
的完整内容,其它的静态文件内容自己可以随便填充。
package main
import (
"embed"
"html/template" "io/fs" "net/http" "os")
var (
//go:embed all:static
assets embed.FS
//go:embed all:templates
templates embed.FS
tpl *template.Template
)
func Assets() (fs.FS, error) {
return fs.Sub(assets, "static")
}
func loadTemplate() {
var err error
tpl, err = template.ParseFS(templates, "templates/layout.html", "templates/dashboard.html")
if err != nil {
panic(err)
}
}
func main() {
mux := http.NewServeMux()
loadTemplate()
sfs, err := Assets()
if err != nil {
panic(err)
}
fileServer := http.FileServer(http.FS(sfs))
mux.Handle("/static/", http.StripPrefix("/static", fileServer))
mux.HandleFunc("/", dashboard)
err = http.ListenAndServe("127.0.0.1:8080", mux)
if err != nil {
os.Exit(255)
return
}
}
func dashboard(writer http.ResponseWriter, request *http.Request) {
err := tpl.ExecuteTemplate(writer, "layout", struct {
}{})
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
}
相信你通过这篇文章对golang的embed
库有个大概了解的用法。
什么时候不使用embed
- 没有嵌入需求
- 文件更改非常平凡(更改后需要重新打包发布)
- 文件非常多且非常大
- 有专门的静态资源服务