BUG-Fly BUG-Fly
  • 首页
  • BUG-EXP
  • 编程开发
  • 电脑评测
  • 生活分享
  • 友情链接
  • Fly全站协议声明
首页 › Go › 你的 Go 程序会吃文件吗

你的 Go 程序会吃文件吗

BUG-Fly
2 年前

在开发程序软件的时候大多情况下会依赖一些静态文件,配置文件。在部署,分发的时候要一般有两种选择:

  1. 部署:把所有依赖以及主体打包成一个压缩文件上传到指定服务器相应目录,再进行解压部署等操作。
  2. 分发给别人:一般会对其制作安装包程序,将主体和依赖进行打包制作安装包,再进行分发给别人使用。

以上方式或多或少有些繁琐。

Go 编译的程序非常适合部署和分发,Go 程序可以被编译成为单体的二进制,这种理想状态是建立在以下两个前提之上:

  1. 没有通过 CGO 引用其他依赖
  2. 开发的程序不会依赖一些静态文件

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 被提了出来:

你的 Go 程序会吃文件吗-BUG-Fly

到后边 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 的形式。

<iframe src="https://carbon.now.sh/embed?bg=rgba%2825%2C32%2C57%2C1%29&t=shades-of-purple&wt=none&l=text%2Fx-go&width=680&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=true&fl=1&fm=MonoLisa&fs=14px&lh=208%25&si=false&es=2x&wm=false&code=package%2520main%250A%250Aimport%2520%28%250A%2509_%2520%2522embed%2522%250A%2509%2522fmt%2522%250A%29%250A%250A%252F%252Fgo%253Aembed%2520hello.txt%250Avar%2520Hello%2520string%250A%250Afunc%2520main%28%29%2520%257B%250A%2509fmt.Println%28Hello%29%250A%257D%250A%252F%252FOutput%253A%2520%25E4%25BD%25A0%25E5%25A5%25BD%25EF%25BC%258CBUG-Fly" style="width: 680px; height: 579px; border:0; transform: scale(1); overflow:hidden;" sandbox="allow-scripts allow-same-origin">

通过 go:embed 指令,在编译后 Hello 的内容就变成了 "你好,BUG-Fly"。

嵌入为 byte slice

还支持将单个文件的内容嵌入为 byte slice

<iframe src="https://carbon.now.sh/embed?bg=rgba%2825%2C32%2C57%2C1%29&t=shades-of-purple&wt=none&l=text%2Fx-go&width=680&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=true&fl=1&fm=MonoLisa&fs=14px&lh=208%25&si=false&es=2x&wm=false&code=package%2520main%250A%250Aimport%2520%28%250A%2520%2520%2520_%2520%2522embed%2522%250A%2520%2520%2520%2522fmt%2522%250A%29%250A%250A%252F%252Fgo%253Aembed%2520hello.txt%250Avar%2520Hello%2520%255B%255Dbyte%250A%250Afunc%2520main%28%29%2520%257B%250A%2520%2520%2520fmt.Println%28Hello%29%250A%257D%250A%252F%252FOutput%253A%2520%255B228%2520189%2520160%2520229%2520165%2520189%2520239%2520188%2520140%252066%252085%252071%252045%252070%2520108%2520121%255D" style="width: 680px; height: 608px; border:0; transform: scale(1); overflow:hidden;" sandbox="allow-scripts allow-same-origin">

嵌入 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

下面对整个文件夹进行嵌入:

<iframe src="https://carbon.now.sh/embed?bg=rgba%2825%2C32%2C57%2C1%29&t=shades-of-purple&wt=none&l=text%2Fx-go&width=680&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=true&fl=1&fm=MonoLisa&fs=14px&lh=208%25&si=false&es=2x&wm=false&code=package%2520main%250A%250Aimport%2520%28%250A%2520%2520%2520%2522embed%2522%250A%2520%2520%2520%2522fmt%2522%250A%29%250A%250A%252F%252Fgo%253Aembed%2520test%250Avar%2520test%2520embed.FS%250A%250Afunc%2520main%28%29%2520%257B%250A%2520%2520%2520file%252C%2520_%2520%253A%253D%2520test.ReadFile%28%2522test%252Fhello2.txt%2522%29%250A%2520%2520%2520fmt.Println%28string%28file%29%29%250A%257D%250A%252F%252F%2520Output%253A%2520%25E5%25A4%259A%25E6%2596%2587%25E4%25BB%25B6%25E6%25B5%258B%25E8%25AF%2595" style="width: 680px; height: 608px; border:0; transform: scale(1); overflow:hidden;" sandbox="allow-scripts allow-same-origin">

在 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 文件夹进行嵌入:

<iframe src="https://carbon.now.sh/embed?bg=rgba%2825%2C32%2C57%2C1%29&t=shades-of-purple&wt=none&l=text%2Fx-go&width=680&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=true&fl=1&fm=MonoLisa&fs=14px&lh=208%25&si=false&es=2x&wm=false&code=package%2520pkg%250A%250Aimport%2520%2522embed%2522%250A%250A%252F%252Fgo%253Aembed%2520static%250Avar%2520Static%2520embed.FS" style="width: 680px; height: 346px; border:0; transform: scale(1); overflow:hidden;" sandbox="allow-scripts allow-same-origin">

此时还需要封装一个文件匹配的 Handler:

<iframe src="https://carbon.now.sh/embed?bg=rgba%2825%2C32%2C57%2C1%29&t=shades-of-purple&wt=none&l=text%2Fx-go&width=889&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=true&fl=1&fm=MonoLisa&fs=14px&lh=208%25&si=false&es=2x&wm=false&code=%252F%252F%2520AssetHandler%2520%25E8%25BF%2594%25E5%259B%259E%25E4%25B8%2580%25E4%25B8%25AA%2520http.Handler%25EF%25BC%258C%25E7%2594%25A8%25E4%25BA%258E%25E4%25BB%258E%2520Assets%2520embed.FS%2520%25E4%25B8%25AD%25E6%258F%2590%25E4%25BE%259B%25E6%2596%2587%25E4%25BB%25B6%25E6%259C%258D%25E5%258A%25A1%25E3%2580%2582%250A%252F%252F%2520%25E5%259C%25A8%25E5%25AE%259A%25E4%25BD%258D%25E6%2596%2587%25E4%25BB%25B6%25E6%2597%25B6%25EF%25BC%258C%25E5%25AE%2583%25E5%25B0%2586%25E4%25BB%258E%25E8%25AF%25B7%25E6%25B1%2582%25E4%25B8%25AD%25E5%2588%25A0%25E9%2599%25A4%25E7%25BB%2599%25E5%25AE%259A%25E7%259A%2584%25E5%2589%258D%25E7%25BC%2580%25EF%25BC%258C%25E5%25B9%25B6%25E5%259C%25A8%25E6%2596%2587%25E4%25BB%25B6%25E7%25B3%25BB%25E7%25BB%259F%25E4%25B8%25AD%25E6%25B7%25BB%25E5%258A%25A0%25E6%25A0%25B9%25E7%259B%25AE%25E5%25BD%2595%25E3%2580%2582%250Afunc%2520AssetHandler%28prefix%2520string%252C%2520assets%2520embed.FS%252C%2520root%2520string%29%2520http.Handler%2520%257B%250A%2520%2520%2520%2520handler%2520%253A%253D%2520fsFunc%28func%28name%2520string%29%2520%28fs.File%252C%2520error%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520assetPath%2520%253A%253D%2520path.Join%28root%252C%2520name%29%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520%252F%252F%2520%25E5%25A6%2582%25E6%259E%259C%25E6%2596%2587%25E4%25BB%25B6%25E4%25B8%258D%25E5%25AD%2598%25E5%259C%25A8%25E6%2597%25B6%25EF%25BC%258Chttp.Handler%2520%25E6%258F%2590%25E4%25BE%259B%25E9%2594%2599%25E8%25AF%25AF%25E5%25A4%2584%25E7%2590%2586%25E6%259C%258D%25E5%258A%25A1%250A%2520%2520%2520%2520%2520%2520%2520%2520file%252C%2520err%2520%253A%253D%2520assets.Open%28assetPath%29%250A%2520%2520%2520%2520%2520%2520%2520%2520if%2520err%2520%21%253D%2520nil%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520return%2520nil%252C%2520err%250A%2520%2520%2520%2520%2520%2520%2520%2520%257D%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520%252F%252F%2520%25E8%25BF%2599%25E6%2598%25AF%25E4%25B8%2580%25E4%25B8%25AA%25E5%2590%2588%25E6%25B3%2595%25E7%259A%2584%25E9%259D%2599%25E6%2580%2581%25E6%2596%2587%25E4%25BB%25B6%25E8%25B7%25AF%25E7%2594%25B1%25E8%25AF%25B7%25E6%25B1%2582%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520file%252C%2520err%250A%2520%2520%2520%2520%257D%29%250A%250A%2520%2520%2520%2520return%2520http.StripPrefix%28prefix%252C%2520http.FileServer%28http.FS%28handler%29%29%29%250A%257D" style="width: 889px; height: 811px; border:0; transform: scale(1); overflow:hidden;" sandbox="allow-scripts allow-same-origin">

注册 http handle:

<iframe src="https://carbon.now.sh/embed?bg=rgba%2825%2C32%2C57%2C1%29&t=shades-of-purple&wt=none&l=text%2Fx-go&width=936&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=true&fl=1&fm=MonoLisa&fs=14px&lh=208%25&si=false&es=2x&wm=false&code=server.Mux.Handle%28%2522%252Fjs%252F%2522%252C%2520utils.AssetHandler%28%2522%252Fjs%252F%2522%252C%2520pkg.Static%252C%2520%2522.%252Fstatic%252Fjs%252F%2522%29%29%250Aserver.Mux.Handle%28%2522%252Fpic%252F%2522%252C%2520utils.AssetHandler%28%2522%252Fpic%252F%2522%252C%2520pkg.Static%252C%2520%2522.%252Fstatic%252Fpic%252F%2522%29" style="width: 936px; height: 229px; border:0; transform: scale(1); overflow:hidden;" sandbox="allow-scripts allow-same-origin">

对于一些知名的 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 中进行了内嵌:

<iframe src="https://carbon.now.sh/embed?bg=rgba%2825%2C32%2C57%2C1%29&t=shades-of-purple&wt=none&l=text%2Fx-go&width=694&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=true&fl=1&fm=MonoLisa&fs=14px&lh=208%25&si=false&es=2x&wm=false&code=package%2520js%250A%250Aimport%2520_%2520%2522embed%2522%250A%250A%252F%252F%2520EncJS%2520%25E9%25A1%25B5%25E9%259D%25A2%25E5%2588%259D%25E5%25A7%258B%25E5%258C%2596%25E5%2590%2591%25E9%25A1%25B5%25E9%259D%25A2%25E4%25B8%25AD%25E6%25B3%25A8%25E5%2585%25A5%25E7%259A%2584%25E5%2588%2586%25E6%259E%2590JS%250A%252F%252Fgo%253Aembed%2520Boom_enc.js%250Avar%2520EncJS%2520string" style="width: 694px; height: 375px; border:0; transform: scale(1); overflow:hidden;" sandbox="allow-scripts allow-same-origin">

使用:

<iframe src="https://carbon.now.sh/embed?bg=rgba%2825%2C32%2C57%2C1%29&t=shades-of-purple&wt=none&l=text%2Fx-go&width=783&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=true&fl=1&fm=MonoLisa&fs=14px&lh=208%25&si=false&es=2x&wm=false&code=%2509_%252C%2520err%2520%253D%2520p.page.EvalOnNewDocument%28js.EncJS%29%250A%2509if%2520err%2520%21%253D%2520nil%2520%257B%250A%2509%2509return%2520errors.Wrap%28err%252C%2520%2522inject%2520enc.js%2520happened%2520error%2522%29%250A%2509%257D" style="width: 783px; height: 317px; border:0; transform: scale(1); overflow:hidden;" sandbox="allow-scripts allow-same-origin">

声明:本站原创文章文字版权归本站所有,转载务必注明作者和出处;本站转载文章仅仅代表原作者观点,不代表本站立场,图文版权归原作者所有。如有侵权,请联系我们删除。
Go
0
0
BUG-Fly
写BUG飞起的Coder
如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求
上一篇

评论 (0)

请登录以参与评论
现在登录
    发表评论

猜你喜欢

  • 如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求
  • JS逆向|某教学CMS(x可思)请求体加密分析

词云

2020 (1) Flask (1) Go (1) JS逆向 (1) Linux (1) Playwright (1) PySide2开发 (1) Python (13) Python实战项目 (5) 固原一中 (1) 国庆70周年 (1) 开源 (1) 数据结构和算法 (2) 数组 (1) 新年贺词 (1) 新月诗刊社 (3) 电脑评测 (3) 软件教程 (3) 雨雯公益 (1) 音乐 (3)

BUG-Fly

写BUG飞起的Coder
34
文章
5
评论
324
获赞
  • 首页
  • 友情链接
Copyright © 2019-08-20-2025 BUG-Fly. Designed by BUG-Fly.

Fly小站已经运行:

津ICP备19007312号
技术基佬基地: KRUNK ZHOU Legna 科技
  • Python13
  • Python实战项目5
  • 新月诗刊社3
  • 音乐3
  • 电脑评测3
  • 首页
  • BUG-EXP
  • 编程开发
  • 电脑评测
  • 生活分享
  • 友情链接
  • Fly全站协议声明