Go 1.16版本新增了Embed功能,这个功能使得开发者可以将资源文件(例如静态文件、配置文件、HTML模板等)打包到可执行二进制文件中,并在运行时通过Go代码轻松访问它们。
Embed功能引入了//go:embed指令。该指令可用于将资源文件嵌入到Go源代码中。语法如下:
//go:embed path/to/file.txt var fileBytes []byte
以上指令将文件"path/to/file.txt"的内容嵌入到变量“fileBytes”中。在使用嵌入式资源之前,需要根据与资源相关联的目录路径生成一个VF(虚拟文件系统)。可以使用以下三个函数之一来生成虚拟文件系统:
FS(内存中的文件系统)
Sub(获得指定目录下的子文件系统)
NewFileStatFromFS(从磁盘或其它来源创建文件)
为了更方便地提供Embed功能,Go提供了一个名为“embed”的标准库。其中,命名类型“embed.FS”表示一个嵌入到程序中的虚拟文件系统。此外,“embed”包还提供了一些方法(如“embed.Dir()”和“embed.ReadFile()”)来访问嵌入式资源。
例如,在使用VS Code安装插件时,往往需要在~/.vscode/extensions下添加扩展文件。如果将所有扩展文件归档为zip,可以使用自带的archive/zip包将zip文件内的所有文件嵌入可执行二进制程序中,如下:
package main import ( "archive/zip" _ "embed" "io/fs" ) //go:embed exts.zip var embededExtFs embed.FS // 声明embed.FS类型的变量,准备解析zip包内的文件 func main() { files, _ := zip.NewReader(bytes.NewReader(embededExtFs), int64(len(embededExtFs))) for _, file := range files.File { // 遍历zip包内的文件列表 fsFile, _ := file.Open() // 打开该文件 fileBytes, _ := io.ReadAll(fsFile) // 以字节流读取文件内容 // ...接下来进行针对性的操作,例如将扩展放到指定路径等... } }
从代码实现上看,Embed功能给Go程序员提供了一种更方便、易用的资源管理方式,特别适用于需要在多台主机之间分发静态资源的情况。
下面展示一个完整用例
// 加载用于静态文件服务和模板解析的gin引擎 func init() { router = gin.Default() // 使用嵌入式文件系统启动静态文件服务 subStatic, _ := fs.Sub(staticFS, "static") // 通过Sub方法获得指定目录下的子文件系统,可在内存中快速访问文件 router.StaticFS("/static", http.FS(subStatic)) // 使用StaticFS方法将静态文件服务挂载到"/static"虚拟路径下 // 使用嵌入式文件系统加载模板 router.HTMLRender = loadTemplates() // 使用自定义多模板渲染器加载模板 } // 程序主入口,设置路由处理函数,返回HTML页面给客户端 func main() { router.GET("/home/index", func(c *gin.Context) { c.HTML(http.StatusOK, "home/index.html", gin.H{}) // 调用Context对象的HTML方法渲染HTML文档,并将结果传递给客户端 }) } // 使用嵌入式文件系统加载HTML模板,并获取其内容 // 并使用multitemplate.Renderer实现多模板功能 func loadTemplates() multitemplate.Renderer { r := multitemplate.NewRenderer() // 新建一个多模板渲染器实例 var fragments string // 读取目录"template/fragment"下的所有子文件(分段HTML代码片段),并拼接为字符串 fragmentFiles, _ := templateFS.ReadDir("template/fragment") for _, fragmentFile := range fragmentFiles { fragmentFilePath := path.Join("template/fragment", fragmentFile.Name()) fragment, _ := templateFS.ReadFile(fragmentFilePath) fragments += string(fragment) } contentDirs, _ := templateFS.ReadDir("template/content") for _, contentDir := range contentDirs { contentDirPath := path.Join("template/content", contentDir.Name()) contentFiles, _ := templateFS.ReadDir(contentDirPath) for _, contentFile := range contentFiles { contentFilePath := path.Join(contentDirPath, contentFile.Name()) tplName := strings.TrimPrefix(contentFilePath, "template/content/") // 减去前缀"template/content/",获取真正的模板名称 content, _ := templateFS.ReadFile(contentFilePath) // 读取目标模板文件 r.AddFromString(tplName, fragments+string(content)) // 向多模板渲染器添加新的模板 } } return r // 返回已加载好的多模板渲染器实例 }
以上代码是一个使用嵌入式文件系统加载静态文件和模板的示例程序。
在init函数中,首先创建了一个默认的gin引擎实例,并使用嵌入式文件系统启动了静态文件服务。这里使用了go 1.16版本新增的Embed功能,将静态文件打包进一个独立的文件系统中,并通过http.FS()函数将其作为参数传递给router.StaticFS()方法来启动静态文件服务。
接着,代码加载模板文件,也是利用Embed打包模板文件,并通过自定义模板引擎的方式实现了多模板功能。其中,模板内容也是从嵌入式文件系统提取出来的,确保代码的便携性。
最后,在main函数中,注册了一个路由处理函数,实现了返回HTML文档的功能。路由处理函数会根据请求中的路径,调用gin.Context对象的HTML方法,将渲染好的HTML页面返回给客户端。
总体上,以上代码展示了如何使用嵌入式文件系统启动静态文件服务和加载html模板,让应用程序更加方便地移植和部署。并且该代码还使用了gin框架的很多特性,例如多模板渲染和路由管理等。
转载请注明:七彩悠悠博客 | 心悠悠 情悠悠 » go语言Embed功能使得开发者可以将资源文件打包到可执行二进制文件中