[译]在go可执行文件中嵌入数据
2017-09-28 13:44
190 查看
原地址https://github.com/XanthusL/blog-gen
原文地址 https://scene-si.org/2017/08/22/embedding-data-in-go-executables/
假如你已经关注了我一段时间,应该知道我正在开发Pendulum编辑器作为每天至少编码一小时的#100DaysOfCode挑战。Pendulum是一个非常适合编辑简单文本和markdown文件的基于web的编辑器。
实际上这篇文章就是用它写的。它由go后端和VueJS前端组成。我希望它易于使用并提供包含一切的单个可执行文件,因此用户不需要下载安装器并解压文件。我需要找个能把所有东西打包到一块的方案。我决定用go-bindata以代码生成的方式来把所有数据通过 go build 添加到可执行文件中。
如果还不熟悉代码生成,你只需要在你代码的某处加上简单的注释,用
执行
运行这个会有预期的输出:
旗开得胜!go generate 很有意思。Node程序通过
例如,genny主要针对强类型代码的生成,因此不再需要手动复制粘贴。不过Have这样的项目更接近Babel对Node的处理–提供转换到go的语言。目前我还不清楚这方面更有吸引力的其他尝试。不过关于Go2及泛型的讨论似乎比较有趣。
这对我们的应用场景来说略显枯燥,我们只是要把一些数据打包到程序中。那么闲话休提,书归正传:
这一行略长,就把它拆分来看:
这会在应用目录下创建一个可以简单的用
实际上,go-bindata-assetfs包已经提供了一个http.FileServer实现。这个用起来就够简单了:
还有一个小问题。我用的是启用了pushHistory的VueJS应用。这就意味着,用户使用时会看到没有释伴符(哈希,#)的类似
这个问题也不难解决。
如果找到了文件,就用预置的ServeHTTP方法取代我自己的实现。采用这种方法只需要对我们之前定义的handler稍作调整:
鉴于此我实现了Pendulum的单个可执行发布版本。可以从GitHub发布页获取并尝试。
编辑:改进serveIndex示例 感谢@Rdihipone
API Foundations in Go
12 Factor Apps with Docker and Go
The SaaS Handbook (work in progress)
I promise you’ll learn a lot more if you buy one. Buying a copy supports me writing more about similar topics. Say thank you and buy my books.
Feel free to send me an email if you want to book my time for consultancy/freelance services. I’m great at APIs, Go, Docker, VueJS and scaling services, among many other things.
原文地址 https://scene-si.org/2017/08/22/embedding-data-in-go-executables/
假如你已经关注了我一段时间,应该知道我正在开发Pendulum编辑器作为每天至少编码一小时的#100DaysOfCode挑战。Pendulum是一个非常适合编辑简单文本和markdown文件的基于web的编辑器。
实际上这篇文章就是用它写的。它由go后端和VueJS前端组成。我希望它易于使用并提供包含一切的单个可执行文件,因此用户不需要下载安装器并解压文件。我需要找个能把所有东西打包到一块的方案。我决定用go-bindata以代码生成的方式来把所有数据通过 go build 添加到可执行文件中。
代码生成?
当然这很简单。例如 go-bindata 工具可以帮我们从public_html目录生成对应的 .go 文件。这对我的应用场景来说是极好的。不过为什么要用bash脚本或者makefile来生成它呢?因为这样我们在执行
go build之前就只需要通过执行一下
go generate利用go的代码生成工具了
如果还不熟悉代码生成,你只需要在你代码的某处加上简单的注释,用
main.go举个例子:
package main //go:generate echo "Hello world" func main() { }
执行
go generate时,可以看到输出了 “Hello world” 。这不是你用 go generate 生成代码的实际需求。你在
//go:generate后面写的一切都会执行。如果你想的话,甚至可以执行
go build。
package main //go:generate echo "Hello world" //go:generate go run main.go func main() { println("Hello world from Go") }
运行这个会有预期的输出:
%go generate Hello world Hello world from Go
旗开得胜!go generate 很有意思。Node程序通过
babel来使Node ES5运行时兼容ES6/ES7的语法。人们正尝试用类似的途径为go提供超出语言目前功能的特性。
例如,genny主要针对强类型代码的生成,因此不再需要手动复制粘贴。不过Have这样的项目更接近Babel对Node的处理–提供转换到go的语言。目前我还不清楚这方面更有吸引力的其他尝试。不过关于Go2及泛型的讨论似乎比较有趣。
这对我们的应用场景来说略显枯燥,我们只是要把一些数据打包到程序中。那么闲话休提,书归正传:
//go:generate go-bindata -prefix front/src -o assets/bindata.go -pkg assets -nomemcopy front/src/dist/...
这一行略长,就把它拆分来看:
//go:generate- 为
go generate作提示
go-bindata- 要执行的主命令
-prefix front/src- 排除“front/src”包
-o assets/bindata.go- 指定输出文件
-pkg assets- 要生成的包名
-nomemcopy- 对内存占用的优化
front/src/dist/...- 要打包的地方
这会在应用目录下创建一个可以简单的用
app/assets导入的
assets包,其中
app对应的是应用目录。
通过HTTP提供嵌入的文件服务
这稍微有点复杂。不过看一下文档之后就简单了。如果要基于本地文件提供服务,你大致需要下面这几行类似的代码:folder := http.Dir("/") server := http.FileServer(folder) http.Handle("/", server)
实际上,go-bindata-assetfs包已经提供了一个http.FileServer实现。这个用起来就够简单了:
import "github.com/elazarl/go-bindata-assetfs" import "app/assets" // ... func main() { // ... files := assetfs.AssetFS{ Asset: assets.Asset, AssetDir: assets.AssetDir, AssetInfo: assets.AssetInfo, Prefix: "dist", } server := http.FileServer(&files) // ... }
还有一个小问题。我用的是启用了pushHistory的VueJS应用。这就意味着,用户使用时会看到没有释伴符(哈希,#)的类似
/blog/about.md的普通链接。这些需要被应用处理的链接内容在asset中并不存在。
这个问题也不难解决。
assetfs.AssetFS结构体有一个
AssetsInfo方法(相当于
os.Stat)和一个
Asset方法(有点像
ioutil.ReadFile)。这使检查一个文件是否存在于asset,若不存在则输出另一个文件成为可能:
// Serves index.html in case the requested file isn't found // (or some other os.Stat error) func serveIndex(serve http.Handler, fs assetfs.AssetFS) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { _, err := fs.AssetInfo(path.Join(fs.Prefix, r.URL.Path)) if err != nil { contents, err := fs.Asset(path.Join(fs.Prefix, "index.html")) if err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } w.Header().Set("Content-Type", "text/html") w.Write(contents) return } serve.ServeHTTP(w, r) } }
如果找到了文件,就用预置的ServeHTTP方法取代我自己的实现。采用这种方法只需要对我们之前定义的handler稍作调整:
http.HandleFunc("/", serveIndex(server, assets))
serveIndex函数返回一个
http.HandlerFunc,这行是相应的修改。这就提供了你用 go generate 和 go-bindata 添加到应用中的数据服务的完整实现。如果你想跳过
//go:generate环节把这些放到CI脚本中也是可以的。
鉴于此我实现了Pendulum的单个可执行发布版本。可以从GitHub发布页获取并尝试。
编辑:改进serveIndex示例 感谢@Rdihipone
当你看到了这里…
要是你能买本我的书定是极好的:API Foundations in Go
12 Factor Apps with Docker and Go
The SaaS Handbook (work in progress)
I promise you’ll learn a lot more if you buy one. Buying a copy supports me writing more about similar topics. Say thank you and buy my books.
Feel free to send me an email if you want to book my time for consultancy/freelance services. I’m great at APIs, Go, Docker, VueJS and scaling services, among many other things.
相关文章推荐
- go语言笔记——go是有虚拟机runtime的,不然谁来做GC呢,总不会让用户自己来new和delete进行内存管理吧,还有反射!Go 的 runtime 嵌入到了每一个可执行文件当中
- Visual Studio 2008 中的可执行文件中嵌入清单文件(manifest) Vista/win7 UAC中把应用程序标注为“需要管理员权限”
- 数据库操作_连接SQL Server数据库示例;连接ACCESS数据库;连接到 Oracle 数据库示例;SqlCommand 执行SQL命令示例;SqlDataReader 读取数据示例;使用DataAdapter填充数据到DataSet;使用DataTable存储数据库表;将数据库数据填充到 XML 文件;10 使用带输入参数的存储过程;11 使用带输入、输出参数的存储过程示;12 获得数据库中表的数目和名称;13 保存图片到SQL Server数据库示例;14 获得插入记录标识号;Exce
- windows笔记-在可执行文件或DLL的多个实例之间共享静态数据
- Delphi向可执行文件EXE尾部写数据然后读取
- 数 4000 据库备份 runtime.exec 去执行命令 返回 process 读取process的输入流 把数据写入执行文件里
- mysql5.6命令行执行sql文件,中文数据乱码解决办法
- SqlCommand执行带GO的SQL脚本文件
- MySQL 执行.sql文件导入数据和执行sql语句
- centos crontab定时器 执行脚本 自动备份文件 同步数据
- 如何在可执行程序中嵌入资源文件(linux环境)
- 数 4000 据库备份 runtime.exec 去执行命令 返回 process 读取process的输入流 把数据写入执行文件里
- t-sql go 重复执行sql 语句 重复插入数据
- 判断本地系统目录下是否存在XML文件,如果不存在就创建一个XMl文件,若存在就在里面执行添加数据
- 用goquery从国家统计局拉取最新省市区3级行政区划代码,生成SQL文件导入数据库
- 数 4000 据库备份 runtime.exec 去执行命令 返回 process 读取process的输入流 把数据写入执行文件里
- 在可执行文件或DLL的多个实例之间共享静态数据
- Matlab读取excel数据,并绘图生成exe可执行文件
- gcc生成二进制文件供uboot的go命令执行
- 大量包含Insert语句的脚本文件批量执行导入数据