提前试用将在 Go1.16 中发布的内嵌静态资源功能( 三 )

DirEntry 是从目录读取的条目(使用 ReadDir 函数或 ReadDirFile 的 ReadDir方法) 。 比如下面 embed 包中的 embed.FS 有一个方法 ReadDir 就返回了 DirEntry 类型的切片 。 这样可以遍历 embed.FS 这个文件系统 。
embed 包资源文件嵌入 Go 二进制程序后 , 我们通过 embed 包可以访问它们 。
string 和 []byte当指令用于 string 或 []byte 时 , 只能有一个模式 , 匹配一个文件 , 字符串或 []byte 的内容是该文件的内容 。 这时虽然不需要使用 embed 包 , 但必须导入 , 因此采用空导入:
import _ "embed" FS(File System)一般内嵌单个文件 , 采用 string 或 []byte 是最好的选择;但内嵌很多文件或目录树 , 应该使用 embed.FS 类型 , 这也是该包目前唯一的类型 。
type FS struct { // The compiler knows the layout of this struct. // See cmd/compile/internal/gc's initEmbed. // // The files list is sorted by name but not by simple string comparison. // Instead, each file's name takes the form "dir/elem" or "dir/elem/". // The optional trailing slash indicates that the file is itself a directory. // The files list is sorted first by dir (if dir is missing, it is taken to be ".") // and then by base, so this list of files: // // p // q/ // q/r // q/s/ // q/s/t // q/s/u // q/v // w // // is actually sorted as: // // p# dir=.elem=p // q/# dir=.elem=q // w/# dir=.elem=w // q/r# dir=qelem=r // q/s/# dir=qelem=s // q/v# dir=qelem=v // q/s/t# dir=q/selem=t // q/s/u# dir=q/selem=u // // This order brings directory contents together in contiguous sections // of the list, allowing a directory read to use binary search to find // the relevant sequence of entries. files *[]file}FS 是文件的只读集合 , 通常使用 //go:embed 指令进行初始化 。 如果不使用 //go:embed 指令声明 FS , 则它是一个空文件系统 。
FS 是只读值 , 因此可以安全地同时使用多个 goroutine , 也可以将 FS 类型的值相互赋值 。
FS 实现了 fs.FS , 因此它可以与任何使用文件系统接口(fs.FS)的包一起使用 , 包括 net/http , text/template 和 html/template 。
此外 , FS 还是实现了 fs.ReadDirFS 和 fs.ReadFileFS 这两个接口 。
所以 , FS 实现了 3 个接口 , 一共 3 个方法:
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)关于它们的用法 , 在上文例子中有所涉及 。
04 实际项目使用本节模拟一个实际项目 , 看怎么使用 embed , 主要两个方面:嵌入静态资源;嵌入模板文件 。 本节示例代码地址: , 采用 Echo 框架 。
因为是演示 embed 的实际用法 , 因此项目做了尽可能简化 , 目录结构如下:
.├── LICENSE├── README.md├── cmd│└── blog│└── main.go├── embed.go├── go.mod├── go.sum├── static│└── css│└── style.min.css└── template└── index.html做个说明:

  • 因为 go:embed 指令只能从相对源码所在目录的位置引用资源 , 这里特意采用了 main.go 放在 cmd/blog 中这种方式 , 看这样如何处理资源嵌入;
  • static 和 template 目录是需要嵌入的目录;
因为 main.go 和 static/template 不在同一个目录 , 因此 main.go 中没法直接使用 go:embed 指令 。 我们在 static 的同级目录下创建一个文件:embed.go , 专门用来写该指令 。 代码如下:
package embedexampleimport ( "embed")//go:embed staticvar StaticAsset embed.FS//go:embed templatevar TemplateFS embed.FS这样 , 项目中所有其他的地方都可以通过引用该包来使用内嵌的资源 。
接着看 main.go 的代码如何使用它的 。
package mainimport ( "html/template" "io" "net/http" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "github.com/polaris1119/embedexample")func main() { e := echo.New() e.Use(middleware.Recover()) e.Use(middleware.Logger()) tpl :=--tt-darkmode-color: #9B9B9B;">模板的引用:
tpl :=--tt-darkmode-color: #9B9B9B;">通过 ParseFS 方法来实现 , 支持 path.Match 格式 。
而静态资源这样引用:
e.GET("/static/*", echo.WrapHandler(http.FileServer(http.FS(embedexample.StaticAsset))))这样 , 在模板文件 index.html 中就可以访问到样式文件了:
可以将编译后的二进制文件移到任何地方 , 然后运行 , 访问 http://localhost:2020 看到如下界面表示成功了 。
提前试用将在 Go1.16 中发布的内嵌静态资源功能文章插图
05 总结本文通过几个例子快速了解官方内嵌静态资源的用法 , 然后讲解一些关键的标准库 , 最后是一个实际项目中使用的例子 。