掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
本文由go語(yǔ)言教程欄目給大家介紹Golang1.16怎么使用embed加載靜態(tài)文件 ,希望對(duì)需要的朋友有所幫助!

成都創(chuàng)新互聯(lián)專業(yè)IDC數(shù)據(jù)服務(wù)器托管提供商,專業(yè)提供成都服務(wù)器托管,服務(wù)器租用,成都棕樹(shù)機(jī)房,成都棕樹(shù)機(jī)房,成都多線服務(wù)器托管等服務(wù)器托管服務(wù)。
embed是什么
embed是在Go 1.16中新加入的包。它通過(guò)//go:embed指令,可以在編譯階段將靜態(tài)資源文件打包進(jìn)編譯好的程序中,并提供訪問(wèn)這些文件的能力。
為什么需要embed
在以前,很多從其他語(yǔ)言轉(zhuǎn)過(guò)來(lái)Go語(yǔ)言的小伙伴會(huì)問(wèn)到,或者踩到一個(gè)坑:就是以為Go語(yǔ)言所打包的二進(jìn)制文件中會(huì)包含配置文件的聯(lián)同編譯和打包。
結(jié)果往往一把二進(jìn)制文件挪來(lái)挪去,就無(wú)法把應(yīng)用程序運(yùn)行起來(lái)了,因?yàn)闊o(wú)法讀取到靜態(tài)文件的資源。
無(wú)法將靜態(tài)資源編譯打包二進(jìn)制文件的話,通常會(huì)有兩種解決方法:
第二種情況的話,Go以前是不支持的,大家就會(huì)借助各種花式的開(kāi)源庫(kù),例如:go-bindata/go-bindata來(lái)實(shí)現(xiàn)。
但是在Go1.16起,Go語(yǔ)言自身正式支持了該項(xiàng)特性。
它有以下優(yōu)點(diǎn)
docker和dockerfile自動(dòng)化前者,這是很麻煩的。通過(guò) 官方文檔 我們知道embed嵌入的三種方式:string、bytes 和 FS(File Systems)。其中string和[]byte類型都只能匹配一個(gè)文件,如果要匹配多個(gè)文件或者一個(gè)目錄,就要使用embed.FS類型。
比如當(dāng)前文件下有個(gè)hello.txt的文件,文件內(nèi)容為hello,world!。通過(guò)go:embed指令,在編譯后下面程序中的s變量的值就變?yōu)榱?code>hello,world!。
package mainimport (
_ "embed"
"fmt")//go:embed hello.txtvar s stringfunc main() {
fmt.Println(s)}
你還可以把單個(gè)文件的內(nèi)容嵌入為slice of byte,也就是一個(gè)字節(jié)數(shù)組。
package mainimport (
_ "embed"
"fmt")//go:embed hello.txtvar b []bytefunc main() {
fmt.Println(b)}
甚至你可以嵌入為一個(gè)文件系統(tǒng),這在嵌入多個(gè)文件的時(shí)候非常有用。
比如嵌入一個(gè)文件:
package mainimport (
"embed"
"fmt")//go:embed hello.txtvar f embed.FSfunc main() {
data, _ := f.ReadFile("hello.txt")
fmt.Println(string(data))}
嵌入本地的另外一個(gè)文件hello2.txt, 支持同一個(gè)變量上多個(gè)go:embed指令(嵌入為string或者byte slice是不能有多個(gè)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))}
當(dāng)前重復(fù)的go:embed指令嵌入為embed.FS是支持的,相當(dāng)于一個(gè):
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))}
Go1.16 為了對(duì) embed 的支持也添加了一個(gè)新包 io/fs。兩者結(jié)合起來(lái)可以像之前操作普通文件一樣。
嵌入的內(nèi)容是只讀的。也就是在編譯期嵌入文件的內(nèi)容是什么,那么在運(yùn)行時(shí)的內(nèi)容也就是什么。
FS文件系統(tǒng)值提供了打開(kāi)和讀取的方法,并沒(méi)有write的方法,也就是說(shuō)FS實(shí)例是線程安全的,多個(gè)goroutine可以并發(fā)使用。
embed.FS結(jié)構(gòu)主要有3個(gè)對(duì)外方法,如下:
// Open 打開(kāi)要讀取的文件,并返回文件的fs.File結(jié)構(gòu).func (f FS) Open(name string) (fs.File, error)// ReadDir 讀取并返回整個(gè)命名目錄func (f FS) ReadDir(name string) ([]fs.DirEntry, error)// ReadFile 讀取并返回name文件的內(nèi)容.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))}
當(dāng)然你也可以像前面的例子一樣寫成多行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系統(tǒng)也采用這個(gè)模式。
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))}
在我們的項(xiàng)目中,是將應(yīng)用的常用的一些配置寫在了.env的一個(gè)文件上,所以我們?cè)谶@里就可以使用go:embed指令。
main.go 文件:
//go:embed ".env" "v1d0/.env"var 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()}
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)} 
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流