在开发程序软件的时候大多情况下会依赖一些静态文件,配置文件。在部署,分发的时候要一般有两种选择:
- 部署:把所有依赖以及主体打包成一个压缩文件上传到指定服务器相应目录,再进行解压部署等操作。
- 分发给别人:一般会对其制作安装包程序,将主体和依赖进行打包制作安装包,再进行分发给别人使用。
以上方式或多或少有些繁琐。
Go 编译的程序非常适合部署和分发,Go 程序可以被编译成为单体的二进制,这种理想状态是建立在以下两个前提之上:
- 没有通过 CGO 引用其他依赖
- 开发的程序不会依赖一些静态文件
Go 常见的开发场景一般是用于服务端开发和命令行工具。对于前者可能会依赖一些静态文件(对于前后端分离架构的站点静态文件会采取 Nginx\Apache 此类的服务器程序进行代理,此种情况下 GO 程序开发的服务是真正的单体二进制。),此时它的部署方便性就会大大折扣。如果有什方法可以将这些静态文件和配置文件也打包进二进制可执行程序内,那可真是泰裤辣!分发和部署的时候只需提供一个二进制就行。
三方库
一些开源项目很早就开始做这方面的工作了:
- rakyll/statik: Embed files into a Go executable (github.com)
- gobuffalo/packr: The simple and easy way to embed static files into Go binaries. (github.com)
- markbates/pkger: Embed static files in Go binaries (replacement for gobuffalo/packr) (github.com)
- knadh/stuffbin: Compress and embed static files and assets into Go binaries and access them with a virtual file system in production (github.com)
虽然这些三方开源库已经都获得了很多 star,也比较成熟了,但毕竟是三方。如果 Go 官方能够下场支持这一特性需求的话,那就泰裤辣。“千呼万唤始出来,犹抱琵琶半遮面”,2019 年末proposal: cmd/go: support embedding static assets (files) in binaries · Issue #35950 · golang/go (github.com) issue 3950 被提了出来:
到后边 Russ Cox 为这一特性需求撰写了一个设计文档:Go command support for embedded static assets (files) — Draft Design (googlesource.com)
并最终在 Go 1.16 里面实现发布了这一特性。下面就来详细介绍一下 go embed
的用法。
embed package
当前文件夹下有一个名为 hello.txt
的文件,内容为:你好,BUG-Fly
:
嵌入为字符串
对于单个文件,支持嵌入为 string 的形式。
通过 go:embed
指令,在编译后 Hello
的内容就变成了 “你好,BUG-Fly”。
嵌入为 byte slice
还支持将单个文件的内容嵌入为 byte slice
嵌入 fs.Fs
embed 甚至还支持嵌入一个文件系统,当需要将多个文件嵌入时就会显得非常有用:
当前目录下有 test 文件夹中有两个文件:hello.txt
和 hello2.txt
│ go.mod
│ main.go
│
├─.idea
│ .gitignore
│ modules.xml
│ test.iml
│ workspace.xml
│
└─test
hello.txt
hello2.txt
下面对整个文件夹进行嵌入:
在 go:embed
指令后边跟上文件夹的路径名称就可以将其嵌入。
- 文件夹分隔符采用正斜杠
/
,即使是windows系统也采用这个模式。 - 使用的是相对路径:相对路径的根路径是 go 源文件所在的文件夹
- 匹配模式:
go:embed
指令中可以只写文件夹名,此文件夹中除了.
和_
开头的文件和文件夹都会被嵌入,并且子文件夹也会被递归的嵌入,形成一个此文件夹的文件系统。如果想嵌入.
和_
开头的文件和文件夹, 比如test 文件夹下的.hello.txt文件,那么就需要使用*
,比如go:embed test/*
。*
不具有递归性。
实践应用
net/http 文件服务
现在有一个使用 net/http
库开发的小型 web 应用,使用到了一些静态资源文件,此时可视使用 embed 对这些静态资源文件使用 embed.Fs
将整个静态文件夹进行嵌入形成一个静态文件服务:
// pkg/static/
├─static
│ ├─html
│ │ login.html
│ │
│ ├─js
│ │ login.js
│ │
│ └─pic
│ background.png
在 pkg/static.go
文件中对 static 文件夹进行嵌入:
此时还需要封装一个文件匹配的 Handler:
注册 http handle:
对于一些知名的 go web 框架(gin、fiber 等)也提供了基于 embed.FS
的文件服务案例,可以在其文档中查阅。
命令行工具程序依赖注入
在我的个人项目 Boom Fly-Playgroud/Boom: Boom 是一款基于无头浏览器的智能 Web 弱口令(后台密码)爆破工具 (github.com)
中的页面分析模块中的一部分代码是由 JS 写的,由 Boom_enc.js
提供能力支持:
└─js
│ Boom.js
│ Boom_enc.js
│ Selector.js
│ static.go
│ stealth.js
为了方便引入注入以及对 Boom_enc.js
文件内容的保护,我对它在 static.go
中进行了内嵌:
使用:
评论 (0)