Use Go Embed
Go1.16引入新的//go:embed指令,可以在编译时嵌入文件和目录,并对其进行访问。通过它,真正做到部署时只有一个二进制文件。
背景:2021-02-16,Go Team正式发布了Go1.16。该版本包含下面的一些重要变化:
- embed 包和 //go:embed 指令
- 增加对 macOS ARM64 的支持
- 默认启用 Module
- io/fs 包
- 弃用io/ioutil
最后,还有许多其他改进和错误修复,包括构建速度提高了 20-25%,linux/amd64上内存使用量减少了 5-15%。有关更改的完整列表以及有关上述改进的更多信息,请参阅 Go 1.16 发行说明。
01 基本使用
Section titled “01 基本使用”基本思路是,在代码中添加特殊注释,Go将知道其包含的一个或多个文件。
Go源文件在引入“embed”包后,可以使用//go:embed指令在编译时,从包目录或者子目录的文件读取内容来初始化
string,[]byte,FS类型的变量。如,可以用下面的三种方式嵌入名为hello.txt文件,然后在运行时打印其内容。
目录结构:
.├── main.go└── hello.txt- 将文件内容嵌入到一个字符串变量
import ( _ "embed" "fmt")
//go:embed hello.txtvar s stringfmt.Print(s)- 将文件内容嵌入到[]byte
import ( _ "embed" "fmt")
//go:embed hello.txtvar b []bytefmt.Print(string(b))- 将一个或多个文件嵌入到文件系统中
import ( _ "embed" "fmt")
//go:embed hello.txtvar f embed.FSdata, _ := f.ReadFile("hello.txt")fmt.Print(string(data))02 可能使用方案
Section titled “02 可能使用方案”package main
import ( _ "embed" "fmt" "strings")
var ( Version string = strings.TrimSpace(version) //go:embed version.txt version string)
func main() { fmt.Printf("Version %q\n", Version)}对于更复杂的示例,我们甚至可以根据是否将某个构建标记传递给go工具来有条件地包含版本信息。
// +build !prod
package main
var version string = "dev"// +build prod
package main
import ( _ "embed")
//go:embed version.txtvar version string$ go run .Version "dev"
$ go run -tags prod .Version "0.0.1"package main
import ( _ "embed" "fmt")
//go:embed quine.govar src string
func main() { fmt.Print(src)}当运行时,就可以将自己打印出来。
Web资源文件
Section titled “Web资源文件”可以网站所需要的所有静态文件或者模板包含在一个可执行文件中,甚至可以通过命令行参数,在读取磁盘文件和读取嵌入文件之间进行切换。
package main
import ( "embed" "io/fs" "log" "net/http" "os")
func main() { useOS := len(os.Args) > 1 && os.Args[1] == "live" http.Handle("/", http.FileServer(getFileSystem(useOS))) http.ListenAndServe(":8888", nil)}
//go:embed staticvar embededFiles embed.FS
func getFileSystem(useOS bool) http.FileSystem { if useOS { log.Print("using live mode") return http.FS(os.DirFS("static")) }
log.Print("using embed mode") fsys, err := fs.Sub(embededFiles, "static") if err != nil { panic(err) }
return http.FS(fsys)}03 注意事项
Section titled “03 注意事项”关于嵌入有些地方需要注意,首先必须要将包导入到任何使用embed命令的文件中。如没有导入包时:
package main
import ( "fmt")
//go:embed file.txtvar s string
func main() { fmt.Print(s)}$ go run mian.gomain.go:7:3: //go:embed only allowed in Go files that import "embed"其次,embed命令只能在包级别变量使用,不能用在函数或方法中。
更多:使用规则,详见文档:https://golang.org/pkg/embed/
相关代码在https://github.com/bytedaring/embed