Aggregator
漏洞管理的“新药”
如何保证数据中心服务器的时间一致 · OSDI 2020
Erasing data from donated devices
安全从业人员应该如何选择一家公司
IOS内核堆风水布局解读
网络知识复习 - Afant1
网商银行安全团队招聘(蚂蚁集团)
用户安全能力进化模型
道理我都懂,但 go embed 究竟该怎么用?
就在前几天,Go 1.16 赶在二月的末尾发布了。
对于这个版本我期待了很久,因为官方终于从语言层面解决了静态文件嵌入的问题—— 加入了 go embed。从此,像 go-bindata、statik、togo 等库都将退出历史的舞台。 同时 Go 1.16 配套的加入了 io/fs 标准库,提供了实现文件系统的接口。同时对 http、embed、os 标准库都加入了对 fs 库的支持。 我记得之前用 togo 做静态资源嵌入时,togo 生成的 .go 文件中是它自己实现了 http/fs 中的 FileSystem 接口,以此实现了一个内部的文件系统。现在可以通过的 io/fs 实现一个基本的文件系统,再通过 http.FS 转换给 http 库使用。可以说 io/fs 库打通了其它标准库中对文件系统转换的需求。
我们常用读写文件的 io/ioutil 库也在 1.16 中做了改动,因为社区反映 ioutil 这个名字模棱两可,遂将 io/ioutil 中的包给废弃了。 具体变动如下:
Before After Discard io.Discard NopCloser io.NopCloser ReadAll io.ReadAll ReadDir os.ReadDir ReadFile os.ReadFile TempDir os.MkdirTemp TempFile os.CreateTemp WriteFile os.WriteFile需要指出的是,上文中我提到的“废弃”,且版本的英文说明用词是Deprecated,但并不意味着 io/ioutil 在未来的 Go 版本中将被移除。 我们仍然可以使用,但是 IDE 会加上横线并提示不推荐使用。Russ Cox 也发推明确说明 io/ioutil 库并不会被“移除”。想想也是,Go 是保证向后兼容的嘛。
以上就是对 Go 1.16 更新的大致介绍,可以看到大多改动都围绕着文件处理。今天想来重点聊聊其中的 go embed,网上关于 go embed 的文章有很多,但是鲜有文章提到 go embed 在我们的实际项目中究竟应该如何使用。
一看就会,一用就废我摸索了挺久才发现一个比较优雅的写法,并成功将 go embed 用到了我前阵子写的 Elaina 中。
在开始介绍之前,我们先来复习一下 go embed 的使用方法、三种数据格式以及对应的注意事项。 go embed 通过注释的形式进行使用。例如:
import ( _ "embed" ) //go:embed readme.md var intro string这样就将 readme.md 文件的内容嵌入到了 intro 变量中。Go 能够允许嵌入的变量类型有如下三种:
变量类型 说明 []byte 用于存储二进制形式的数据,比如图片、富媒体等。 string 用于存储 UTF-8 编码的字符串。 embed.FS 用于嵌入多个文件和目录的结构。如果变量类型有误,程序将在编译期间报错。
需要特别注意的是: `go embed` 仅能嵌入当前目录及其子目录,无法嵌入上层目录。同时也不支持软链接。
更绝的是,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 }我原本还想着通过 go embed 在程序编译时读取 .git/config 配置敏感信息的…… 💔
三种嵌入文件的情况在 Elaina 项目中使用 go emebd 时,我遇到了三种不同的目录结构,这三种目录结构也大致囊括了我们在实际项目会遇到的场景。这里分享一下我的做法。
嵌入多个文件在一个 Web 应用项目中常会有 templates 目录,存放了 HTML 的模板文件,它们常以 .tmpl 或者 .html 作为后缀名。
. ├── sandbox.tmpl └── sandbox_404.tmpl要嵌入这些模板文件,我们可以在 templates 目录下创建一个 fs.go 文件:
package templates import ( "embed" ) //go:embed *.tmpl var FS embed.FS这样就将所有的 .tmpl 后缀的文件嵌入进了 FS 变量中。 后面在路由中使用 html/template 库来从文件系统中加载并解析模板。以下是在 Gin 框架中的示例:
tpl := template.Must(template.New("").ParseFS(templates.FS, "*")) r.SetHTMLTemplate(tpl) 嵌入多个目录一个 Web 应用项目下往往还会有个 public 目录,其用于存储所有的静态资源。目录下会有诸如 css js assets 这样的子目录。
. ├── css │ └── sandbox.css └── js └── sandbox.js这一次是嵌入多个目录,我们可以效仿上面的做法,在 public 目录下创建一个 fs.go 文件:
package public import ( "embed" ) //go:embed css js var FS embed.FS在注册路由时,Gin 的 StaticFS 需要一个实现了 http.fs 中 FileSystem 接口的变量。这里我们使用 http.FS 方法,将 fs.FS 转换成 FileSystem。二者其实都是只需实现 Open(name string) (File, error) 这个方法即可。
r.StaticFS("/static", http.FS(public.FS)) 嵌入子目录有时我们的项目是前后端分离的,需要将打包编译好的前端嵌入进来。编译好的前端往往会在 dist 目录下。
. ├── css │ ├── app.3ca5488f.css │ └── chunk-vendors.08a0794a.css ├── index.html ├── js │ ├── app.1bdd8cf2.js │ ├── app.1bdd8cf2.js.map │ ├── chunk-2d0ac239.c72b0c7d.js │ ├── chunk-2d0ac239.c72b0c7d.js.map ├── manifest.json ├── precache-manifest.a2e4eb7c729e7ecf28ada54a6ea672b4.js └── service-worker.js而我们并不能效仿前两种情况,创建一个 fs.go 文件在 dist 目录下。原因有两点:
- dist 目录往往是写在 .gitignore 中被忽略的。
- dist 中既有文件又有目录,若指定其嵌入 * 的话,fs.go 文件也会被嵌入进来。
因此这里我们将 fs.go 放置于 dist 的父目录中。文件内容还是类似的:
package frontend import ( "embed" ) //go:embed dist var FS embed.FS需要注意的是,若直接使用 `frontend.FS` 注册路由,所有的文件路径都会有 `dist/` 前缀。我们需要通过形如 `http://localhost:8080/dist/index.html` 的地址进行访问,这显然不是我们想要的。 因此,这里需要使用 fs.Sub() 方法,来进入 frontend.FS 的下层文件夹,并返回一个新的 FS。
fe, err := fs.Sub(frontend.FS, "dist") if err != nil { log.Fatal("Failed to sub path `dist`: %v", err) } r.StaticFS("/m", http.FS(fe))其实前两种情形都可以用这第三种 fs.Sub() 进入目录来解决(即分别进入 templates public 目录)。 但这将失去变量 templates.FS public.FS 这些清晰易懂的包名命名。
总结以上就是我摸索出的 go embed 在实际项目中的使用方式。可能不大准确,欢迎大家纠正以及提出你所认为的最佳实践。
科幻百科 | 2020年科幻出版物数据分析报告
Out of My Depth (Where I Belong)
Why ping uses UDP port 1025
Solving DevSecOps and Microservice Challenges with Zero Trust
Active Cyber Defence (ACD) - The Third Year
Struts2 系列漏洞 - S2-032
Massive Campaign Targeting UK Banks Bypassing 2FA
Can Edge Computing Exist Without the Edge? Part 3: The Economics of Edge Computing
Our Role in Inclusion, Diversity, and Engagement
Millions Affected by Malware Attributed to Android Barcode-Scanning App
We’ve all come to a realization that we don’t go anywhere without our phone. It’s a utility that helps us...
The post Millions Affected by Malware Attributed to Android Barcode-Scanning App appeared first on McAfee Blog.