go embed包 初学习

embed包 诞生的历史

出现的问题

很多从其他语言转过来 Go 语言的同学会问到,或是踩到一个坑。就是以为 Go 语言所打包的二进制文件中会包含配置文件的联同编译和打包。

结果往往一把二进制文件挪来挪去,就无法把应用程序运行起来了。因为无法读取到静态文件的资源。

无法将静态资源编译打包进二进制文件的话,通常会有两种解决方法:

第一种是识别这类静态资源,是否需要跟着程序走。
第二种就是考虑将其打包进二进制文件中

第二种情况的话,Go 以前是不支持的,大家就会去借助各种花式的开源库,例如:go-bindata/go-bindata 来实现,

第三方包的数量繁多,对于开发选择增加了开发者的心智负担,需要go官方处理这个问题

embed包 诞生

Go编译的程序非常适合部署,如果没有通过CGO引用其它的库的话,我们一般编译出来的可执行二进制文件都是单个的文件,非常适合复制和部署。在实际使用中,除了二进制文件,可能还需要一些配置文件,或者静态文件,比如html模板、静态的图片、CSS、javascript等文件,如何这些文件也能打进到二进制文件中,那就太美妙,我们只需复制、按照单个的可执行文件即可。

一些开源的项目很久以前就开始做这方面的工作,比如 gobuffalo/packrmarkbates/pkgerrakyll/statikknadh/stuffbin 等等,但是不管怎么说这些都是第三方提供的功能,如果Go官方能内建支持就好了。2019末一个提案被提出 issue#35950,期望Go官方编译器支持嵌入静态文件。后来Russ Cox专门写了一个设计文档 Go command support for embedded static assets, 并最终实现了它。

embed包 使用

hello.txt 内容

hello word gopher!!

main.go文件

package main

import (
	_ "embed"
	"fmt"
)

//go:embed hello.txt
var info string

func main() {
	fmt.Println(info)
}

打印内容

PS D:\coder\go-coder\package-learn\embed-package-learn> go run .\main.go
hello word gopher!!

通过 //go:embed 指令 嵌套 hello.txt 文件内容,把hello.txt里面的内容 嵌套在 下面声明的 info 字符串变量中,打印 info 变量内为hello.txt 的内容 hello word gopher!!

Go 能够允许嵌入的变量类型有如下三种:

变量类型说明
[]byte用于存储二进制形式的数据,比如图片、富媒体等
string用于存储 UTF-8 编码的字符串。
embed.FS用于嵌入多个文件和目录的结构。
embed 嵌入变量 []byte 字节切片
package main

import (
	_ "embed"
	"fmt"
)

//go:embed hello.txt
var info []byte

func main() {

	//输出的字节内容
	fmt.Println(info)
	//[]byte 转成string方式输出
	fmt.Println(string(info))

}

输出打印

PS D:\coder\go-coder\package-learn\embed-package-learn> go run .\main.go
[104 101 108 108 111 32 119 111 114 100 32 103 111 112 104 101 114 33 33]
hello word gopher!!

使用 embed.FS (用于嵌入多个文件和目录的结构)

main1,go

package main

import (
	"embed"
	"fmt"
)

//go:embed hello.txt hello2.txt public static/*

var f embed.FS

func main() {

	data, error := f.ReadFile("hello.txt")

	fmt.Println("\nReadFile hello.txt\n", string(data), error)

	data1, error1 := f.ReadFile("static/1.txt")

	fmt.Println("\nReadFile static/1.txt\n", string(data1), error1)

	dir, error := f.ReadDir("static")
	fmt.Println("\nReadDir static\n", dir, error)

	data2, error2 := f.Open("static")
	fmt.Println("\nOpen static\n", data2, error2)

}
PS D:\coder\go-coder\package-learn\embed-package-learn> go run .\main1.go

ReadFile hello.txt        
 hello word gopher!! <nil>

ReadFile static/1.txt      
 this is statis 1.txt <nil>

ReadDir static
 [0x96aba8 0x96abd8] <nil>

Open static
 &{0x96ab48 [{static/1.html  [28 79 59 189 103 3 227 235 101 4 11 55 102 144 70 2
19]} {static/1.txt this is statis 1.txt [232 249 104 174 107 128 13 4 161 54 167 
245 137 117 177 185]}] 0} <nil>

1.//go:embed 指令 支持多行 或者 空格间隔 多嵌入的方式
2. //go:embed 可以直接写 目录名 嵌套所有目录的内容不包括点符号 . 和下划线 _ 目录名嵌套同样不支持空目录嵌套,可以使用目录/* 的方式贪婪嵌套所有文件推荐使用 目录/* 贪婪的模式进行匹配嵌套

3.go emebd 禁止嵌入如 .git .svn 这些目录,官方认为这些目录不属于 package 的一部分,如果嵌入则会在编译时报错。可参见 Go 源码src/cmd/go/internal/load/pkg.go#L2091-2107


// isBadEmbedName reports whether name is the base name of a file that
// can't or won't be included in modules and therefore shouldn't be treated
// as existing for embedding.
func isBadEmbedName(name string) bool {
    if err := module.CheckFilePath(name); err != nil {
        return true
    }
    switch name {
    // Empty string should be impossible but make it bad.
    case "":
        return true
    // Version control directories won't be present in module.
    case ".bzr", ".hg", ".git", ".svn":
        return true
    }
    return false
}

embed.FS 也能调各类文件系统的接口,其实本质是 embed.FS 实现了 io/fs 接口。

只读属性

在 embed 所提供的 FS 中,我们可以发现其都是打开和只读方法:

    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) 

只读不写的设计说明在 embed.FS多个goroutine中使用它是安全的 同时也安全地将类型FS的值分配给彼此

注意正确使用

  1. //go:embed 嵌套的内容路径必须是 当前路径或当前子目录,嵌套的文件目录必须存在,不支持嵌套上级目录的文件
    (1) 文件不存在情况
package main

import (
	_ "embed"
	"fmt"
)

//go:embed hello1.txt
var info string

func main() {
	fmt.Println(info)
}

打印报错

PS D:\coder\go-coder\package-learn\embed-package-learn> go run .\main.go
main.go:8:12: pattern hello1.txt: no matching files found

(2) 嵌套上级目录文件

package main

import (
	_ "embed"
	"fmt"
)

//go:embed ../embed-package-learn/hello.txt
var info string

func main() {
	fmt.Println(info)
}

打印报错

PS D:\coder\go-coder\package-learn\embed-package-learn> go run .\main.go
main.go:8:12: pattern ../embed-package-learn/hello.txt: invalid pattern syntax

  1. //go:embed 必选在包级别变量声明,不能在函数中声明 //go:embed
package main

import (
	_ "embed"
	"fmt"
)

func main() {
	//go:embed hello.txt
	var info string
	fmt.Println(info)
}

打印报错

PS D:\coder\go-coder\package-learn\embed-package-learn> go run .\main.go
# command-line-arguments
.\main.go:9:4: go:embed cannot apply to var inside func

补充更多

更多详细内容还是看官网练习比较好 go官网embed链接

连接教程还都是 英文的,对于咱们英语小白来说 粘贴出来到翻译网站翻译或截图翻译,阅读效率还真是低下,我用的是谷歌浏览器,谷歌浏览器自带的翻译插件支持 整个页面自动翻译,现在被墙了,其他浏览器翻译插件 要不 不好用,要不还要收费,花钱是不可能的,推荐一个腾讯浏览器翻译插件,自己测试阅读英文网站,嘎嘎好使
腾讯翻译浏览器插件下载地址 https://www.chajianxw.com/product-tool/44019.html

引用的链接

转载自:简书-大地缸 Go1 16新特性一文快速上手 Go Embed
转载自:简书-大地缸 Go Embed 简明教程
转载自:Light Cube - 道理我都懂,但 go embed 究竟该怎么用?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值