Go1.16 新特性:一文快速上手 Go embed
本文轉(zhuǎn)載自微信公眾號(hào)「腦子進(jìn)煎魚了」,作者陳煎魚。轉(zhuǎn)載本文請(qǐng)聯(lián)系腦子進(jìn)煎魚了公眾號(hào)。
大家好,我是正在沉迷學(xué)習(xí)煎魚的煎魚。
在以前,很多從其他語言轉(zhuǎn)過來 Go 語言的同學(xué)會(huì)問到,或是踩到一個(gè)坑。就是以為 Go 語言所打包的二進(jìn)制文件中會(huì)包含配置文件的聯(lián)同編譯和打包。
結(jié)果往往一把二進(jìn)制文件挪來挪去,就無法把應(yīng)用程序運(yùn)行起來了。因?yàn)闊o法讀取到靜態(tài)文件的資源。
無法將靜態(tài)資源編譯打包進(jìn)二進(jìn)制文件的話,通常會(huì)有兩種解決方法:
- 第一種是識(shí)別這類靜態(tài)資源,是否需要跟著程序走。
- 第二種就是考慮將其打包進(jìn)二進(jìn)制文件中。
第二種情況的話,Go 以前是不支持的,大家就會(huì)去借助各種花式的開源庫,例如:go-bindata/go-bindata 來實(shí)現(xiàn)。
但從在 Go1.16 起,Go 語言自身正式支持了該項(xiàng)特性,今天我們將通過這篇文章快速了解和學(xué)習(xí)這項(xiàng)特性。
基本使用
演示代碼:
- import _ "embed"
- //go:embed hello.txt
- var s string
- func main() {
- print(s)
- }
我們首先在對(duì)應(yīng)的目錄下創(chuàng)建了 hello.txt 文件,并且寫入文本內(nèi)容 “吃煎魚”。
在代碼中編寫了最為核心的 //go:embed hello.txt 注解。注解的格式很簡(jiǎn)單,就是 go:embed 指令聲明,外加讀取的內(nèi)容的地址,可支持相對(duì)和絕對(duì)路徑。
輸出結(jié)果:
- 吃煎魚
讀取到靜態(tài)文件中的內(nèi)容后自動(dòng)賦值給了變量 s,并且在主函數(shù)中成功輸出。
而針對(duì)其他的基礎(chǔ)類型,Go embed 也是支持的:
- //go:embed hello.txt
- var s string
- //go:embed hello.txt
- var b []byte
- //go:embed hello.txt
- var f embed.FS
- func main() {
- print(s)
- print(string(b))
- data, _ := f.ReadFile("hello.txt")
- print(string(data))
- }
輸出結(jié)果:
- 吃煎魚
- 吃煎魚
- 吃煎魚
我們同時(shí)在一個(gè)代碼文件中進(jìn)行了多個(gè) embed 的注解聲明。
并且針對(duì) string、slice、byte、fs 等多種類型進(jìn)行了打包,也不需要過多的處理,非常便利。
拓展用法
除去基本用法完,embed 本身在指令上也支持多種變形:
- //go:embed hello1.txt hello2.txt
- var f embed.FS
- func main() {
- data1, _ := f.ReadFile("hello1.txt")
- fmt.Println(string(data1))
- data2, _ := f.ReadFile("hello2.txt")
- fmt.Println(string(data2))
- }
在指定 go:embed 注解時(shí)可以一次性多個(gè)文件來讀取,并且也可以一個(gè)變量多行注解:
- //go:embed hello1.txt
- //go:embed hello2.txt
- var f embed.FS
也可以通過在注解中指定目錄 helloworld,再對(duì)應(yīng)讀取文件:
- //go:embed helloworld
- var f embed.FS
- func main() {
- data1, _ := f.ReadFile("helloworld/hello1.txt")
- fmt.Println(string(data1))
- data2, _ := f.ReadFile("helloworld/hello2.txt")
- fmt.Println(string(data2))
- }
同時(shí)既然能夠支持目錄讀取,也能支持貪婪模式的匹配:
- //go:embed helloworld/*
- var f embed.FS
可能會(huì)有小伙伴注意到,embed.FS 也能調(diào)各類文件系統(tǒng)的接口,其實(shí)本質(zhì)是 embed.FS 實(shí)現(xiàn)了 io/fs 接口。
只讀屬性
在 embed 所提供的 FS 中,我們可以發(fā)現(xiàn)其都是打開和只讀方法:
- type FS
- 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)
根據(jù)此也可以確定 embed 所打包進(jìn)二進(jìn)制文件的內(nèi)容只允許讀取,不允許變更。
更抽象來講就是在編譯期就確定了 embed 的內(nèi)容,在運(yùn)行時(shí)不允許修改,保證了一致性。
總結(jié)
- 通過 Go1.16 正式提供的 embed 特性,可以實(shí)現(xiàn)原生就支持靜態(tài)資源文件的嵌入。整體如下:
- 在功能上:能夠?qū)㈧o態(tài)資源嵌入二進(jìn)制文件中,在運(yùn)行時(shí)可以打開和讀取相關(guān)的打包后的靜態(tài)文件。
- 在安全上:是在編譯期編譯嵌入,在運(yùn)行時(shí)不支持修改。
- 在使用上:
- 支持單文件讀?。篻o:embed hello.txt。
- 支持多文件讀?。篻o:embed hello1.txt、go:embed hello2.txt。
- 支持目錄讀?。篻o:embed helloworld。
- 支持貪婪匹配:go:embed helloworld/*。
總的來講,Go1.16 embed 特性很好的填補(bǔ)了 Go 語言在打包靜態(tài)文件資源的一塊原生空白領(lǐng)域。同時(shí)也說明了 Go 官方的確在不斷地吸收社區(qū)的一些良好的想法和經(jīng)驗(yàn)。