go1.16 出了 go:embed
指令, 了解后发现很强!!!
作用
go:embed
是一个指令,用来嵌入静态文件到二进制文件当中。
我们在部署时会打包成一个二进制文件,如果应用包含一些配置文件比如 config.ini
/ index.html
config.yaml
等一系列静态文件就比较麻烦了,在以往的做法就是将这些静态文件放到二进制同一个目录里头,通过文件系统进行读取, 或者是写在 .go
文件里, 就很不方便。
现在可以通过 go:embed
指令将这些静态文件打包在二进制文件里,也就是只有一个二进制文件。
规则
在使用 go:embed
指令之前先了解一下有哪些限制。
- 如果
go:embed
指令跟着单个文件,只支持string
/[]byte
/embed.FS
- 如果
go:embed
指令跟着多个文件或文件夹,只支持embed.FS
go:embed
指令下一行必须紧跟着嵌入变量- 只支持全局变量
- 只支持
string
/[]byte
/embed.FS
, 其他类型包括别名都不支持 - 嵌入文件路径不支持绝对路径,不支持路径中包含
.
/..
, 当前路径使用*
来个典型的例子
2.txt 文件内容:
hello golang
package main
import (
_ "embed"
"fmt"
)
//go:embed 2.txt
var s string
func main() {
fmt.Println(s)
// hello golang
}
注意 //
注释后面不能有空格
[]byte
字节切片类型:
package main
import (
_ "embed"
"fmt"
)
//go:embed 2.txt
var b []byte
func main() {
fmt.Println(b)
// [104 101 108 108 111 32 103 111 108 97 110 103]
}
嵌入文件系统
package main
import (
"embed"
"fmt"
)
//go:embed 2.txt
var f embed.FS
func main() {
s, _ := f.ReadFile("2.txt")
fmt.Println(string(s))
// hello golang
}
嵌入多个文件
当嵌入多个文件只能使用 embed.FS
类型,不支持其他类型。
package main
import (
"embed"
"fmt"
)
//go:embed 1.txt 2.txt
var f embed.FS
func main() {
s1, _ := f.ReadFile("1.txt")
s2, _ := f.ReadFile("2.txt")
fmt.Println(string(s1))
fmt.Println(string(s2))
}
或者指令分开多行写也可以
package main
import (
"embed"
"fmt"
)
//go:embed 1.txt
//go:embed 2.txt
var f embed.FS
func main() {
s1, _ := f.ReadFile("1.txt")
s2, _ := f.ReadFile("2.txt")
fmt.Println(string(s1))
fmt.Println(string(s2))
}
FS 是只读的
FS 目前提供了3个API:
- func (f FS) Open(name string) (fs.File, error)
- func (f FS) ReadDir(name string) ([]fs.DirEntry, error)
- func (f FS) ReadFile(name string) ([]byte, error)
这3个都是只读的,没有可写的接口, 也就是不存在竞态问题,可以并发读取。
embed.FS 实现了 1.16 io/fs.FS
新的接口:
package main
import (
"embed"
"fmt"
"io/fs"
)
//go:embed 1.txt
var f embed.FS
func main() {
content, _ := fs.ReadFile(f, "1.txt")
fmt.Println(string(content))
}
嵌入文件夹
采用 /
斜杠来访问目录下的文件,windows
也一样。
当嵌入的是文件夹时除了 .
和 _
开头的文件和文件夹都会被嵌入即使在程序中没有使用。
package main
import (
"embed"
"fmt"
"io/fs"
)
//go:embed test
var f embed.FS
func main() {
content, _ := fs.ReadFile(f, "test/1.txt")
fmt.Println(string(content))
// hello golang
}