什么是embed
在Golang 1.16版本中,新增了一个大家期待已久的特性//go:embed,它的作用就是可以在Go语言应用程序中包含任何文件、目录的内容,也就是说我们可以把文件以及目录中的内容都打包到生成的Go语言应用程序中了,部署的时候,直接扔一个二进制文件就可以了,不用再包含一些静态文件了,因为它们已经被打包到生成的应用程序中了。
基本用法
通过 官方文档 我们知道embed嵌入的三种方式:string
、bytes
和FS
(File Systems)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//go:embed 基本用法是:
package main
import "embed"
//go:embed hello.txt
var s string
//go:embed hello.txt
var b []byte
//go:embed hello.txt
//go:embed assets
var f embed.FS
func main() {
print(s)
print(string(b))
data, _ := f.ReadFile("hello.txt")
print(string(data))
}
|
1、导入 embed 包,如果没有使用 embed.FS 需要显示的导入:
2、匹配文件 //go:embed <匹配模式> <匹配模式>...
,匹配模式符合 path.Match 方式。
(1)匹配模式是相对位置,如:
1
2
3
4
5
6
|
├── assets
│ ├── .gitkeep
│ ├── _home.html
│ └── index.html
├── hello.txt
└── main.go
|
匹配 index.html
则使用 //go:embed assets/index.html
即可,不能使用 .
和 ..
(如./hello.txt
)。
(2)可以匹配多个,以空格隔开,如 //go:embed hello.txt assets/index.html
。也可以重复,避免匹配长度过长:
1
2
3
|
//go:embed hello.txt
//go:embed assets/index.html
var f embed.FS
|
(3)[]byte 和 string 只能匹配单个文件。如果文件名称有空格可使用双引号 " 或者反引号 ``。
(4)如果//go:embed assets
匹配的是一个目录,那么该目录中所有文件都将递归的嵌入,除了以.或开头的文件。
(5)匹配目录中的所有内容,使用统配*,包括以.和` 开头的文件。
3、匹配的变量只能是全局变量
进阶用法
Go1.16 为了对 embed 的支持也添加了一个新包 io/fs。两者结合起来可以像之前操作普通文件一样。
常规文件操作
如通过 embed 进行常规的文件目录读取,文件递归遍历等:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//go:embed hello.txt
//go:embed hello.txt assets/*
var f embed.FS
...
entries, err := f.ReadDir(".")
if err != nil {
panic(err)
}
for _, entry := range entries {
info, err := entry.Info()
if err != nil {
panic(err)
}
fmt.Println(info.Name(), info.Size(), info.IsDir())
}
|
Web文件系统
通过原生go http服务,我们将静态资源文件嵌入到二进制中,做静态文件服务器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package main
import (
"embed"
"net/http"
)
//go:embed hello.txt assets/*
var f embed.FS
func main() {
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(f))))
http.ListenAndServe(":8080", nil)
}
|
以上代码的核心除了//go:embed
指令外,还有通过http.FS这个函数,把embed.FS类型的static转换为http.FileServer函数可以识别的http.FileSystem类型。
通过常见的Web服务框架,提供文件的访问:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package main
import (
"embed"
"net/http"
"github.com/gin-gonic/gin"
)
//go:embed hello.txt assets/*
var f embed.FS
func main() {
e := gin.Default()
e.StaticFS("/static/", http.FS(f))
e.Run(":8080")
}
|
其它web框架各自可以试试。
模版操作
通过 embed 方式嵌入模版,渲染模版:
1
2
3
4
|
├── main.go
└── tmpl
├── en.tmpl
└── zh.tmpl
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package main
import (
"embed"
"fmt"
"html/template"
"net/http"
)
//go:embed tmpl/*.tmpl
var f embed.FS
func main() {
t, err := template.ParseFS(f, "tmpl/*.tmpl")
if err != nil {
panic(err)
}
// /hello?lang=xx.tmpl
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
t.ExecuteTemplate(w, r.FormValue("lang"), nil)
})
http.ListenAndServe(":8080", nil)
}
|
template包提供了ParseFS函数,可以直接从一个embed.FS中加载模板,然后用于HTTP Web中。