最近需要使用 golang1.16 中的功能 embed ,本文简单记录下 embed 的使用。

embed 介绍

Go 1.16 引入了embed包,允许我们在编译时将静态文件(例如 .go、.html、.css、.js 等)嵌入到 Go 源文件中。这在构建静态网站、单页应用程序(SPA)和其他项目时非常有用。

主要有几个优点:

  • 方便部署:不需要再部署静态资源文件,所有的资源都直接嵌入到可执行文件中。

  • 安全:用户无法直接访问或修改嵌入的文件。

  • 版本管理:和Go代码一起版本控制。

使用

嵌入为字符串

可以将文件内容保存到字符串变量中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import (
	_ "embed"
	"fmt"
)

//go:embed hello.txt
var s string

func main() {
	fmt.Println(s)
}

文件路径下有个 hello.txt,内容如下:hello, overstarry,代码运行输出:hello, overstarry

保存为 []bytes

还可以将文件内容保存为 []bytes变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import (
	_ "embed"
	"fmt"
)

//go:embed hello.txt
var s []byte

func main() {
	fmt.Println(string(s))
}

运行代码输出内容与上面一致。

保存为 fs.FS 类型

还可以将文件保存为 fs.FS 类型,这在嵌入多个文件时非常有用(string和[]bytes不支持多个//go:embed指令)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
	"embed"
	"fmt"
)

//go:embed hello.txt
//go:embed hello2.txt
var f embed.FS

func main() {
	data, _ := f.ReadFile("hello.txt")
	fmt.Println(string(data))
	data, _ = f.ReadFile("hello2.txt")
	fmt.Println(string(data))
}

嵌入的内容是只读的。也就是在编译期嵌入文件的内容是什么,那么在运行时的内容也就是什么。

FS文件系统值提供了打开和读取的方法,并没有write的方法,也就是说FS实例是线程安全的,多个goroutine可以并发使用。

单个文件嵌入为多个变量

还可以将单个文件保存为多个变量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main
import (
	_ "embed"
	"fmt"
)
//go:embed hello.txt
var s string
//go:embed hello.txt
var s2 string
func main() {
	fmt.Println(s)
	fmt.Println(s2)
}

将 hello.txt的内容保存为 s和s2变量以供不同地方使用。

文件嵌入为可导出变量

可以将文件嵌入为可导出变量和不可导出变量,以应对需要外部调用的场景。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main
import (
	_ "embed"
	"fmt"
)
//go:embed hello.txt
var S string
//go:embed hello.txt
var s2 string
func main() {
	fmt.Println(S)
	fmt.Println(s2)
}

嵌入多个文件

go:embed指令支持嵌入多个文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main
import (
	"embed"
	"fmt"
)
//go:embed hello.txt hello2.txt
var f embed.FS
func main() {
	data, _ := f.ReadFile("hello.txt")
	fmt.Println(string(data))
	data, _ = f.ReadFile("hello2.txt")
	fmt.Println(string(data))
}

嵌入文件夹

除了支持文件嵌入还可以嵌入整个文件夹。需要注意的是文件夹分隔符使用/,windows环境也是。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main
import (
	"embed"
	"fmt"
)
//go:embed p
var f embed.FS
func main() {
	data, _ := f.ReadFile("p/hello.txt")
	fmt.Println(string(data))
	data, _ = f.ReadFile("p/hello2.txt")
	fmt.Println(string(data))
}

小结

除了上面介绍的一些功能,embed还支持相对路径等功能,感兴趣的读者可以自行查阅相关资料。

参考