溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

golang embed是什么

發(fā)布時(shí)間:2021-11-12 15:02:59 來(lái)源:億速云 閱讀:150 作者:iii 欄目:編程語(yǔ)言

本篇內(nèi)容主要講解“golang embed是什么”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“golang embed是什么”吧!                          

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à),通常會(huì)有兩種解決方法:

  • 第一種是識(shí)別這類(lèi)靜態(tài)資源,是否需要跟著程序走。

  • 第二種就是將其打包進(jìn)二進(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)

  • 能夠?qū)㈧o態(tài)資源打包到二進(jìn)制包中,部署過(guò)程更簡(jiǎn)單。傳統(tǒng)部署要么需要將靜態(tài)資源與已編譯程序打包在一起上傳,或者使用dockerdockerfile自動(dòng)化前者,這是很麻煩的。

  • 確保程序的完整性。在運(yùn)行過(guò)程中損壞或丟失靜態(tài)資源通常會(huì)影響程序的正常運(yùn)行。

  • 靜態(tài)資源訪問(wèn)沒(méi)有io操作,速度會(huì)非???/strong>。

embed基礎(chǔ)用法

通過(guò) 官方文檔 我們知道embed嵌入的三種方式:string、bytes 和 FS(File Systems)。其中string[]byte類(lèi)型都只能匹配一個(gè)文件,如果要匹配多個(gè)文件或者一個(gè)目錄,就要使用embed.FS類(lèi)型。

特別注意:embed這個(gè)包一定要導(dǎo)入,如果導(dǎo)入不使用的話(huà),使用 _ 導(dǎo)入即可

一、嵌入為字符串

比如當(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)}
二、嵌入為byte slice

你還可以把單個(gè)文件的內(nèi)容嵌入為slice of byte,也就是一個(gè)字節(jié)數(shù)組。

package mainimport (
    _ "embed"
    "fmt")//go:embed hello.txtvar b []bytefunc main() {
    fmt.Println(b)}
三、嵌入為fs.FS

甚至你可以嵌入為一個(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))}

embed進(jìn)階用法

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í)例是線(xiàn)程安全的,多個(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)
二、嵌入多個(gè)文件

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)然你也可以像前面的例子一樣寫(xiě)成多行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))}

應(yīng)用

在我們的項(xiàng)目中,是將應(yīng)用的常用的一些配置寫(xiě)在了.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()}

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)}

到此,相信大家對(duì)“golang embed是什么”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI