惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Security Latest
Security Latest
P
Palo Alto Networks Blog
AWS News Blog
AWS News Blog
NISL@THU
NISL@THU
T
Threatpost
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Latest news
Latest news
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
WordPress大学
WordPress大学
J
Java Code Geeks
P
Privacy International News Feed
阮一峰的网络日志
阮一峰的网络日志
S
Schneier on Security
博客园 - 聂微东
Project Zero
Project Zero
美团技术团队
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Scott Helme
Scott Helme
I
Intezer
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hacker News: Front Page
S
Security @ Cisco Blogs
博客园 - 司徒正美
O
OpenAI News
Last Week in AI
Last Week in AI
L
LINUX DO - 热门话题
酷 壳 – CoolShell
酷 壳 – CoolShell
SecWiki News
SecWiki News
月光博客
月光博客
S
Security Affairs
The GitHub Blog
The GitHub Blog
P
Privacy & Cybersecurity Law Blog
S
Secure Thoughts
V
V2EX
S
Securelist
F
Fortinet All Blogs
W
WeLiveSecurity
D
Docker
博客园 - 三生石上(FineUI控件)
Simon Willison's Weblog
Simon Willison's Weblog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
V
Visual Studio Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Webroot Blog
Webroot Blog
Engineering at Meta
Engineering at Meta

Deepzz's Blog

如何写出高质量的 Skill | Deepzz's Blog 什么是Skill,它能迸发出什么能力 | Deepzz's Blog iOS 实用有趣的快捷指令 & 自动化收集 | Deepzz's Blog 从零到日入$10:朋友独立开发临时邮箱网站的故事 | Deepzz's Blog 2024年终总结 - 稳中求变 | Deepzz's Blog 2023年终总结 - 焦虑的一年 | Deepzz's Blog 如何玩转智能家居 - 组建局域网,访问家庭网络 | Deepzz's Blog 如何玩转智能家居 - 网络组网方案实施 | Deepzz's Blog 如何玩转智能家居 - 家庭组网方案选择 | Deepzz's Blog 如何玩转智能家居 - 灯控方案选择 | Deepzz's Blog 如何玩转智能家居 - HomeAssistant接触 | Deepzz's Blog 如何玩转智能家居 - HomeAssistant介绍 | Deepzz's Blog 跨省异地换工作,社保、医保、公积金转移 | Deepzz's Blog 10 分钟理解什么是 OpenID Connect(OIDC) 协议 | Deepzz's Blog 上海之行,告一段落 | Deepzz's Blog nginx server 如何写,如何写 nginx 配置 | Deepzz's Blog 她和他的第一次出国自由行 - 巴厘岛 | Deepzz's Blog 她和他的第一次出国自由行 - 出行模版 | Deepzz's Blog Go 测试,go test 工具的具体指令 flag | Deepzz's Blog Go 单元测试,基准测试,http 测试 | Deepzz's Blog 10 分钟理解什么是 OAuth 2.0 协议 | Deepzz's Blog
Go1.16初体验,怎样使用 //go:embed | Deepzz's Blog
2021-02-19 · via Deepzz's Blog

2月16日,Go1.16版本发布了。对于我们普通开发者来说,本次版本发布了一些有趣的特性,这里列举了重要的几点:

  • 新增了embed包,在编译时通过使用 //go:embed 指令可以进行嵌入文件的访问,即将文件嵌入到二进制包中。
  • 增加了对 macOS ARM64 的支持(Apple silicon)。
  • 默认开启 Go modules。
  • 修复了一些bug和改进一些问题,如构建速度提升25%,内存使用量降低15%。
  • io/util 包被弃用,所有方法被移至 ioos 包。

具体详细的发布日志移步:go1.16,本篇文章关注的是如何使用://go:embed

embed功能说明

embed能帮我们做什么?一句话概括就是将静态资源文件嵌入到编译的二进制文件中。

这样做有什么优势?个人认为比较重要是保证一个应用的完整性。比如:

  1. 比如一个Web应用,包含了很多image和html,一般情况下我们需要将所有的文件和编译好的二进制文件拷贝到同一机器。如果是分布式应用还会带来更多的拷贝过程。当然如果使用如docker容器镜像方式,是会简化拷贝过程,但也会增加一些负担,如:打包过程。
  2. 比如一个App应用,本身会携带很多如音频、图片小文件。一般情况下在安装过程中我们需要将许许多多的小文件进行拷贝,我们知道磁盘I/O瓶颈比较大的,安装时间长会给用户带来不好的体验。
  3. 比如一个游戏应用。
  4. 比如一个WebAssembly应用等等。

当然上面举的例子只是从一些方面来考虑,具体的打包部署方式需要综合考虑多个因素,如当前公司的自动化运维体系。

embed能帮我们保证一个应用的整体性和完整性,我觉得对于强迫症的开发者来说一定是个福利,哈哈。下面来看看embed的使用方法。

embed基础用法

通过 官方文档 我们知道embed嵌入的三种方式:string、bytes和FS(File Systems)。

//go:embed 基本用法是:

package main

import "embed"

//go:embed hello.txt
var s string

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

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

func main() {
  print(s)

  print(string(b))

  data, _ := f.ReadFile("hello.txt")
  print(string(data))
}

1、导入 embed 包,如果没有使用 embed.FS 需要显示的导入:

import _ "embed"

2、匹配文件 //go:embed <匹配模式> <匹配模式>...,匹配模式符合 path.Match 方式。

(1)匹配模式是相对位置,如:

├── assets
│   ├── .gitkeep
│   ├── _home.html
│   └── index.html
├── hello.txt
└── main.go

匹配 index.html 则使用 //go:embed assets/index.html 即可,不能使用 ...(如./hello.txt)。
(2)可以匹配多个,以空格隔开,如 //go:embed hello.txt assets/index.html。也可以重复,避免匹配长度过长:

//go:embed hello.txt
//go:embed assets/index.html
var f embed.FS

(3)[]bytestring 只能匹配单个文件。如果文件名称有空格可使用双引号 " 或者反引号 ``。 (4)如果//go:embed assets匹配的是一个目录,那么该目录中所有文件都将递归的嵌入,除了以.开头的文件。 (5)匹配目录中的所有内容,使用统配*,包括以.` 开头的文件。
3、匹配的变量只能是全局变量。

embed进阶用法

Go1.16 为了对 embed 的支持也添加了一个新包 io/fs。两者结合起来可以像之前操作普通文件一样。

常规文件操作

如通过 embed 进行常规的文件目录读取,文件递归遍历等:

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

...

entries, err := f.ReadDir(".")
if err != nil {
  panic(err)
}
for _, entry := range entries {
  info, err := entry.Info()
  if err != nil {
    panic(err)
  }
  fmt.Println(info.Name(), info.Size(), info.IsDir())
}

Web文件系统

通过原生go http服务,我们将静态资源文件嵌入到二进制中,做静态文件服务器:

package main

import (
  "embed"
  "net/http"
)

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

func main() {
  http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(f))))

  http.ListenAndServe(":8080", nil)
}

通过常见的Web服务框架,提供文件的访问:

package main

import (
  "embed"
  "net/http"

  "github.com/gin-gonic/gin"
)

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

func main() {
  e := gin.Default()

  e.StaticFS("/static/", http.FS(f))
  e.Run(":8080")
}

其它web框架各自可以试试。

模版操作

通过 embed 方式嵌入模版,渲染模版:

├── main.go
└── tmpl
    ├── en.tmpl
    └── zh.tmpl
package main

import (
    "embed"
    "fmt"
    "html/template"
    "net/http"
)

//go:embed tmpl/*.tmpl
var f embed.FS

func main() {
  t, err := template.ParseFS(f, "tmpl/*.tmpl")
  if err != nil {
    panic(err)
  }

  // /hello?lang=xx.tmpl
  http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()

    t.ExecuteTemplate(w, r.FormValue("lang"), nil)
  })

  http.ListenAndServe(":8080", nil)
}

版本嵌入

通常我们需要将我们的版本打包到二进制文件中,以便确定我们的版本信息。embed 之前我们可以采取通过 -ldflags 的方法将版本动态的赋值到变量。现在,我们可以通过 embed 方式赋值啦。

# version_dev.go
// +build !prod

package main

var version string = "dev"
# version_prod.go
// +build prod

package main

import (
  _ "embed"
)

//go:embed version.txt
var version string

执行命令:

$ go run .
Version "dev"

$ go run -tags prod .
Version "0.0.1"

参考文档

[1] https://pkg.go.dev/embed
[2] How to Use //go:embed

本文链接:https://deepzz.com/post/how-to-use-go-embed.html参与评论 »

--EOF--

发表于 2021-02-19 14:27:00

本站使用「署名 4.0 国际」创作共享协议,转载请注明作者及原网址。更多说明 »

提醒:本文最后更新于 1941 天前,文中所描述的信息可能已发生改变,请谨慎使用。