embed是什么
//go:embed
为什么需要embed
在以前,很多从其他语言转过来Go语言的小伙伴会问到,或者踩到一个坑:就是以为Go语言所打包的二进制文件中会包含配置文件的联同编译和打包。
结果往往一把二进制文件挪来挪去,就无法把应用程序运行起来了,因为无法读取到静态文件的资源。
无法将静态资源编译打包二进制文件的话,通常会有两种解决方法:
- 第一种是识别这类静态资源,是否需要跟着程序走。
- 第二种就是将其打包进二进制文件中。
go-bindata/go-bindata
但是在Go1.16起,Go语言自身正式支持了该项特性。
它有以下优点
dockerdockerfile
embed基础用法
string[]byteembed.FS
特别注意:embed这个包一定要导入,如果导入不使用的话,使用 _ 导入即可
一、嵌入为字符串
hello,world!go:embedhello,world!
package main
import (
_ "embed"
"fmt"
)
//go:embed hello.txt
var s string
func main() {
fmt.Println(s)
}
二、嵌入为byte slice
你还可以把单个文件的内容嵌入为slice of byte,也就是一个字节数组。
package main
import (
_ "embed"
"fmt"
)
//go:embed hello.txt
var b []byte
func main() {
fmt.Println(b)
}
三、嵌入为fs.FS
甚至你可以嵌入为一个文件系统,这在嵌入多个文件的时候非常有用。
比如嵌入一个文件:
package main
import (
"embed"
"fmt"
)
//go:embed hello.txt
var f embed.FS
func main() {
data, _ := f.ReadFile("hello.txt")
fmt.Println(string(data))
}
go:embedgo:embed
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))
}
go:embed
package main
import (
"embed"
"fmt"
)
//go:embed hello.txt
//go:embed hello.txt
var f embed.FS
func main() {
data, _ := f.ReadFile("hello.txt")
fmt.Println(string(data))
}
还可以嵌入子文件夹下的文件:
package main
import (
"embed"
"fmt"
)
//go:embed p/hello.txt
//go:embed p/hello2.txt
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进阶用法
embedio/fs
一、只读
嵌入的内容是只读的。也就是在编译期嵌入文件的内容是什么,那么在运行时的内容也就是什么。
FS文件系统值提供了打开和读取的方法,并没有write的方法,也就是说FS实例是线程安全的,多个goroutine可以并发使用。
embed.FS结构主要有3个对外方法,如下:
// Open 打开要读取的文件,并返回文件的fs.File结构.
func (f FS) Open(name string) (fs.File, error)
// ReadDir 读取并返回整个命名目录
func (f FS) ReadDir(name string) ([]fs.DirEntry, error)
// ReadFile 读取并返回name文件的内容.
func (f FS) ReadFile(name string) ([]byte, error)
二、嵌入多个文件
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))
}
go:embed
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))
}
三、支持文件夹
/
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))
}
应用
go:embed
main.go
//go:embed ".env" "v1d0/.env"
var FS embed.FS
func main(){
setting.InitSetting(FS)
manager.InitManager()
cron.InitCron()
routersInit := routers.InitRouter()
readTimeout := setting.ServerSetting.ReadTimeout
writeTimeout := setting.ServerSetting.WriteTimeout
endPoint := fmt.Sprintf(":%d", setting.ServerSetting.HttpPort)
maxHeaderBytes := 1 << 20
server := &http.Server{
Addr: endPoint,
Handler: routersInit,
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
MaxHeaderBytes: maxHeaderBytes,
}
server.ListenAndServe()
}
setting.go
func InitSetting(FS embed.FS) {
// 总配置处理
var err error
data, err := FS.ReadFile(".env")
if err != nil {
log.Fatalf("Fail to parse '.env': %v", err)
}
cfg, err = ini.Load(data)
if err != nil {
log.Fatal(err)
}
mapTo("server", ServerSetting)
ServerSetting.ReadTimeout = ServerSetting.ReadTimeout * time.Second
ServerSetting.WriteTimeout = ServerSetting.WriteTimeout * time.Second
// 分版本配置引入
v1d0Setting.InitSetting(FS)
}
本作品采用《CC 协议》,转载必须注明作者和本文链接