首頁 > 軟體

Go1.16新特性embed打包靜態資原始檔實現

2022-07-22 18:00:03

背景

相信有一部分人喜愛 GO 的初衷大概是:跨平臺靜態編譯,如果在沒用通過 CGO 參照其他庫的話,一般編譯出來的可執行二進位制檔案部署起來非常方便,但人們在實際中發現,使用 Go 語言開發的後端 WEB 程式存在 HTML 模版、圖片、JS、CSS、JSON 等靜態資源,部署時需要把這些靜態資源與二進位制程式一起上傳到伺服器部署,在現今遍地花容器的今天,為了簡化部署流程,能不能更進一步的把這些靜態檔案與二進位制程式一起打包起來部署,那樣豈不是更方便?對運維來說打包一體化帶來的好處就是運維複雜度的降低,對技術團隊來說打包一體化還可以保證程式完整性可控性。

因此,GO 社群發起了一個期望 Go 編譯器支援嵌入靜態檔案的提案issue#35950。現在,這個功能將隨著 1.16 版本一起釋出,目前最新的版本是 Go 1.16 RC1 預覽版。

好了,接下來我們詳細介紹 go embed 的各個功能。

embed 嵌入

└── cmd 測試目錄
    ├── assets 靜態資源目錄
    │   ├── .idea.txt
    │   ├── golang.txt
    │   └── hello.txt
    └── main.go  測試go原始檔

字串、位元組切片、檔案嵌入

package main
import (
	"embed"
	_ "embed"
	"fmt"
)
//go:embed指令用來嵌入,必須緊跟著嵌入後的變數名
//只支援嵌入為string, byte slice和embed.FS三種型別,這三種型別的別名(alias)和命名型別(如type S string)都不可以
//以字串形式嵌入 assets/hello.txt
//go:embed assets/hello.txt
var s string
//檔案的內容嵌入為slice of byte,也就是一個位元組陣列
//go:embed assets/hello.txt
var b []byte
//嵌入為一個檔案系統 新的檔案系統FS
//go:embed assets/hello.txt
//go:embed assets/golang.txt
var f embed.FS
func main() {
	fmt.Println("embed string.", s)
	fmt.Println("embed byte.", string(b))
	data, _ := f.ReadFile("assets/hello.txt")
	fmt.Println("embed fs.", string(data))
	data, _ = f.ReadFile("assets/golang.txt")
	fmt.Println("embed fs.", string(data))
}

編譯執行後輸出:

embed string. hello golang!
embed byte. hello golang!
embed fs. hello golang!
embed fs. hello!

從上面的程式碼可以看出,embed 支援嵌入為 string,byte slice 和 embed.FS 這三種型別,另外也不允許從這些型別中派生哦。

嵌入檔案

對於 FS 型別的嵌入,也可以支援一個變數嵌入多個檔案。

//go:embed assets/hello.txt
//go:embed assets/golang.txt
var f embed.FS

當然也支援,兩個變數嵌入一個檔案。雖然兩個變數嵌入了同一個檔案,但它們在編譯的時候會獨立分配,彼此之間並不會互相影響。

嵌入資料夾

FS 型別的嵌入支援資料夾.分隔符采用正斜槓/,即使是 windows 系統也採用這個模式。

//go:embed assets
var f embed.FS
func main() {
	data, _ := f.ReadFile("assets/hello.txt")
	fmt.Println(string(data))
}

嵌入匹配

go:embed 指令中可以只寫資料夾名,此資料夾中除了.和_開頭的檔案和資料夾都會被嵌入,並且子資料夾也會被遞迴的嵌入,形成一個此資料夾的檔案系統。

如果想嵌入.和_開頭的檔案和資料夾, 比如.hello.txt 檔案,那麼就需要使用*,比如 go:embed assets/*。

不具有遞迴性,所以子資料夾下的.和_不會被嵌入,除非你在專門使用子資料夾的進行嵌入:

├── assets
│   ├── .idea.txt
│   ├── golang.txt
│   └── hello.txt
└── main.go
package main
import (
	"embed"
	_ "embed"
	"fmt"
)
//go:embed assets/*
var f embed.FS
func main() {
	data, _ := f.ReadFile("assets/.idea.txt")
	fmt.Println(string(data))
}

FS 檔案系統

embed.FS 實現了 io/fs.FS 介面,它可以開啟一個檔案,返回 fs.File:

package main
import (
	"embed"
	_ "embed"
	"fmt"
)
//go:embed assets
var f embed.FS
func main() {
	helloFile, _ := f.Open("assets/hello.txt")
	stat, _ := helloFile.Stat()
	fmt.Println(stat.Name(), stat.Size())
}

它還提供了 ReadFileh 和 ReadDir 功能,遍歷一個檔案下的檔案和資料夾資訊:

package main
import (
	"embed"
	_ "embed"
	"fmt"
)
//go:embed assets
var f embed.FS
func main() {
	dirEntries, _ := f.ReadDir("assets")
	for _, de := range dirEntries {
		fmt.Println(de.Name(), de.IsDir())
	}
}

因為它實現了 io/fs.FS 介面,所以可以返回它的子資料夾作為新的檔案系統:

package main
import (
	"embed"
	_ "embed"
	"fmt"
	"io/fs"
	"io/ioutil"
)
//go:embed assets
var f embed.FS
func main() {
	as, _ := fs.Sub(f, "assets")
	hi, _ := as.Open("hello.txt")
	data, _ := ioutil.ReadAll(hi)
	fmt.Println(string(data))
}

總結:

  • 對於單個的檔案,支援嵌入為字串和 byte slice
  • 對於多個檔案和資料夾,支援嵌入為新的檔案系統 FS
  • go:embed 指令用來嵌入,必須緊跟著嵌入後的變數名
  • 只支援嵌入為 string, byte slice 和 embed.FS 三種型別,型別派生也不可以。

以上就是Go 1.16新特性embed打包靜態資原始檔實現的詳細內容,更多關於Go embed打包靜態資源的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com