Golang一般用到embed package的主要场景就是Web server需要携带html css js等静态文件。

使用embed directive可以让外部文件在编译时自动加入到二进制程序中。这样我们如果要分发程序,只需一个二进制文件,不需要外带任何其他资源文件。

而我们在代码中去引用embedded文件有三种方法(类型):string , []byteFS

但实际上,embed还是有一些限制的,本文就详细讲讲这些限制是什么,以及如何曲线救国。

文件层级问题

如果embed directive与被embedded的文件不在同一级(文件系统中)。

比如说

│  main.go
│  
├─server
│      handler.go
│      
└─tepl
        index.html

如果handler.go试图去包含tepl文件夹里的文件:

// handler.go
//go:embed tepl
var s embed.FS

embed pattern将无法被成功解析,因为被引用者的文件层级高于引用者。

除了将被引用者的文件层级降至同一级或更低,还有一种方法就是直接在main.go这样同级的文件中进行处理,然后传递给需要的包中。

// main.go
//go:embed tepl
var tepl embed.FS

func main() {
	handler.injectFS(&tepl)
}

目前来说,没有其他更好的解决方法了。

复杂路径问题

假如我们在tepl中放入一个static文件夹:

│  main.go
│  
└─server
    │  handler.go
    │  
    └─tepl
        │  index.html
        │  
        └─static
                main.css

在handler.go中,我们用http.FileServer来处理对静态文件们的请求:

//go:embed tepl
var tepl embed.FS

func handle() {
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(tepl))))
}

很自然地,定义的是/static/的路由,而不是/,因为根路径常常要处理其他事情。

但此时由于 embed 的局限,如果我们要获取mian.css,就需要访问这个路径/static/tepl/static/main.css

解决的方法也很简单,用 io.fs 包中的Sub方法即可:

var tepl embed.FS

func handle() {
	sub, _ := fs.Sub(tepl, "tepl/static")
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(sub))))
}

Refs


https://pkg.go.dev/embed