本文由go语言教程栏目给大家介绍golang1.16怎么使用embed加载静态文件 ,希望对需要的朋友有所帮助!
embed是什么
embed是在go 1.16中新加入的包。它通过//go:embed指令,可以在编译阶段将静态资源文件打包进编译好的程序中,并提供访问这些文件的能力。
为什么需要embed
在以前,很多从其他语言转过来go语言的小伙伴会问到,或者踩到一个坑:就是以为go语言所打包的二进制文件中会包含配置文件的联同编译和打包。
结果往往一把二进制文件挪来挪去,就无法把应用程序运行起来了,因为无法读取到静态文件的资源。
无法将静态资源编译打包二进制文件的话,通常会有两种解决方法:
第一种是识别这类静态资源,是否需要跟着程序走。第二种就是将其打包进二进制文件中。第二种情况的话,go以前是不支持的,大家就会借助各种花式的开源库,例如:go-bindata/go-bindata来实现。
但是在go1.16起,go语言自身正式支持了该项特性。
它有以下优点
能够将静态资源打包到二进制包中,部署过程更简单。传统部署要么需要将静态资源与已编译程序打包在一起上传,或者使用docker和dockerfile自动化前者,这是很麻烦的。确保程序的完整性。在运行过程中损坏或丢失静态资源通常会影响程序的正常运行。静态资源访问没有io操作,速度会非常快。embed基础用法通过 官方文档 我们知道embed嵌入的三种方式:string、bytes 和 fs(file systems)。其中string和[]byte类型都只能匹配一个文件,如果要匹配多个文件或者一个目录,就要使用embed.fs类型。
特别注意:embed这个包一定要导入,如果导入不使用的话,使用 _ 导入即可
一、嵌入为字符串比如当前文件下有个hello.txt的文件,文件内容为hello,world!。通过go:embed指令,在编译后下面程序中的s变量的值就变为了hello,world!。
package mainimport ( _ embed fmt)//go:embed hello.txtvar s stringfunc main() { fmt.println(s)}
二、嵌入为byte slice你还可以把单个文件的内容嵌入为slice of byte,也就是一个字节数组。
package mainimport ( _ embed fmt)//go:embed hello.txtvar b []bytefunc main() { fmt.println(b)}
三、嵌入为fs.fs甚至你可以嵌入为一个文件系统,这在嵌入多个文件的时候非常有用。
比如嵌入一个文件:
package mainimport ( embed fmt)//go:embed hello.txtvar f embed.fsfunc main() { data, _ := f.readfile(hello.txt) fmt.println(string(data))}
嵌入本地的另外一个文件hello2.txt, 支持同一个变量上多个go:embed指令(嵌入为string或者byte slice是不能有多个go:embed指令的):
package mainimport ( embed fmt)//go:embed hello.txt//go:embed hello2.txtvar f embed.fsfunc main() { data, _ := f.readfile(hello.txt) fmt.println(string(data)) data, _ = f.readfile(hello2.txt) fmt.println(string(data))}
当前重复的go:embed指令嵌入为embed.fs是支持的,相当于一个:
package mainimport ( embed fmt)//go:embed hello.txt//go:embed hello.txtvar f embed.fsfunc main() { data, _ := f.readfile(hello.txt) fmt.println(string(data))}
还可以嵌入子文件夹下的文件:
package mainimport ( embed fmt)//go:embed p/hello.txt//go:embed p/hello2.txtvar f embed.fsfunc main() { data, _ := f.readfile(p/hello.txt) fmt.println(string(data)) data, _ = f.readfile(p/hello2.txt) fmt.println(string(data))}
embed进阶用法go1.16 为了对 embed 的支持也添加了一个新包 io/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 mainimport ( embed fmt)//go:embed hello.txt hello2.txtvar f embed.fsfunc main() { data, _ := f.readfile(hello.txt) fmt.println(string(data)) data, _ = f.readfile(hello2.txt) fmt.println(string(data))}
当然你也可以像前面的例子一样写成多行go:embed:
package mainimport ( embed fmt)//go:embed hello.txt//go:embed hello2.txtvar f embed.fsfunc main() { data, _ := f.readfile(hello.txt) fmt.println(string(data)) data, _ = f.readfile(hello2.txt) fmt.println(string(data))}
三、支持文件夹文件夹分隔符采用正斜杠/,即使是windows系统也采用这个模式。
package mainimport ( embed fmt)//go:embed pvar f embed.fsfunc main() { data, _ := f.readfile(p/hello.txt) fmt.println(string(data)) data, _ = f.readfile(p/hello2.txt) fmt.println(string(data))}
应用在我们的项目中,是将应用的常用的一些配置写在了.env的一个文件上,所以我们在这里就可以使用go:embed指令。
main.go 文件:
//go:embed .env v1d0/.envvar fs embed.fsfunc 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)}
以上就是embed是啥?go怎么用它加载静态文件?的详细内容。