Heim >Backend-Entwicklung >Golang >Was ist Einbetten? Wie lädt Go damit statische Dateien?
Dieser Artikel wurde von der Go-SpracheTutorial-Kolumne geschrieben, um Ihnen vorzustellen, wie Sie mit Embed statische Dateien in Golang1.16 laden. Ich hoffe, dass er Freunden in Not hilfreich sein wird!
Warum müssen Sie einbetten?
In der Vergangenheit fragten viele Freunde, die von anderen Sprachen zur Go-Sprache wechselten, oder traten in eine Falle: Sie dachten, dass die Binärdatei von Go gepackt wurde Die Sprache würde die Konfigurationsdatei enthalten. Gemeinsame Kompilierung und Verpackung. //go:embed
指令,可以在编译阶段将静态资源文件打包进编译好的程序中,并提供访问这些文件的能力。
为什么需要embed
在以前,很多从其他语言转过来Go语言的小伙伴会问到,或者踩到一个坑:就是以为Go语言所打包的二进制文件中会包含配置文件的联同编译和打包。
结果往往一把二进制文件挪来挪去,就无法把应用程序运行起来了,因为无法读取到静态文件的资源。
无法将静态资源编译打包二进制文件的话,通常会有两种解决方法:
第二种情况的话,Go以前是不支持的,大家就会借助各种花式的开源库,例如:go-bindata/go-bindata
来实现。
但是在Go1.16起,Go语言自身正式支持了该项特性。
它有以下优点
docker
和dockerfile
自动化前者,这是很麻烦的。通过 官方文档 我们知道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)}
你还可以把单个文件的内容嵌入为slice of byte,也就是一个字节数组。
package mainimport ( _ "embed" "fmt")//go:embed hello.txtvar b []bytefunc main() { fmt.Println(b)}
甚至你可以嵌入为一个文件系统,这在嵌入多个文件的时候非常有用。
比如嵌入一个文件:
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))}
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))}
文件夹分隔符采用正斜杠/
go-bindata/go-bindata
, um dies zu erreichen . 🎜🎜Aber ab Go 1.16 unterstützt die Go-Sprache selbst diese Funktion offiziell. 🎜🎜Es hat die folgenden Vorteile:🎜docker
und dockerfile
, um Ersteres zu automatisieren, was umständlich ist. string
und []byte
nur mit einer Datei übereinstimmen. Wenn Sie mehrere Dateien oder ein Verzeichnis abgleichen möchten, müssen Sie embed.FS verwenden
Typ. 🎜🎜Besonderer Hinweis: Wenn der Import nicht verwendet wird, verwenden Sie einfach _ zum Importieren🎜
hello, world!
. Durch die go:embed
-Direktive wird der Wert der s-Variablen im folgenden Programm nach der Kompilierung zu hello, world!
. 🎜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))}
//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)}🎜Eine andere lokale Datei hello2.txt einbetten, mehrere
go:embed
-Anweisungen für dieselbe Variable unterstützen (eingebettet als String oder Byte-Slice kann nicht mehrere haben go:embed
-Anweisung):🎜rrreee🎜Die aktuell wiederholte go:embed
-Anweisung wird als embed.FS unterstützt, was einer:🎜rrreee🎜Es kann auch Dateien unter eingebettet werden Unterordner: 🎜rrreeeio/fs zur Unterstützung von <code>embed
hinzu. Das Kombinieren der beiden kann wie zuvor normale Dateien funktionieren. 🎜go:embed
:🎜rrreee/
, auch bei Windows-Systemen Benutze auch dieses Modell. 🎜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/.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)}
Das obige ist der detaillierte Inhalt vonWas ist Einbetten? Wie lädt Go damit statische Dateien?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!