如何将文件嵌入Golang二进制文件?

Zvi*_*ika 40 binaries go

我有一些我从Go程序中读取的文本文件.我想发送一个可执行文件,而不另外提供该文本文件.如何将其嵌入到Windows和Linux的编译中?

Joh*_*röm 47

从Go 1.4开始,如果您需要更多灵活性,可以使用go generate.

如果您有多个文本文件或文本文件可能更改,您可能不想硬编码文本文件,但在编译时包含它.

如果您有以下文件:

main.go
scripts/includetxt.go
a.txt
b.txt
Run Code Online (Sandbox Code Playgroud)

并且想要访问main.go中所有.txt文件的内容,您可以包含一个包含go generate命令的特殊注释.

main.go

package main

import "fmt"

//go:generate go run scripts/includetxt.go

func main() {
    fmt.Println(a)
    fmt.Println(b)
}
Run Code Online (Sandbox Code Playgroud)

go generate命令将在之后运行脚本go:generate.在这种情况下,它运行一个go脚本,它读取所有文本文件并将它们作为字符串文字输出到一个新文件中.我跳过错误处理更短的代码.

脚本/ includetxt.go

package main

import (
    "io"
    "io/ioutil"
    "os"
    "strings"
)

// Reads all .txt files in the current folder
// and encodes them as strings literals in textfiles.go
func main() {
    fs, _ := ioutil.ReadDir(".")
    out, _ := os.Create("textfiles.go")
    out.Write([]byte("package main \n\nconst (\n"))
    for _, f := range fs {
        if strings.HasSuffix(f.Name(), ".txt") {
            out.Write([]byte(strings.TrimSuffix(f.Name(), ".txt") + " = `"))
            f, _ := os.Open(f.Name())
            io.Copy(out, f)
            out.Write([]byte("`\n"))
        }
    }
    out.Write([]byte(")\n"))
}
Run Code Online (Sandbox Code Playgroud)

要将所有.txt文件编译到exectutable中:

$ go generate
$ go build -o main
Run Code Online (Sandbox Code Playgroud)

现在您的目录结构如下所示:

main.go
main
scripts/includetxt.go
textfiles.go
a.txt
b.txt
Run Code Online (Sandbox Code Playgroud)

其中textfiles.go由go generate和script/includetxt.go生成

textfiles.go

package main 

const (
a = `hello`
b = `world`
)
Run Code Online (Sandbox Code Playgroud)

并且运行main给出了

$ ./main
hello
world
Run Code Online (Sandbox Code Playgroud)

只要您编码UTF8编码文件,这将正常工作.如果要对其他文件进行编码,则可以使用go语言(或任何其他工具)的全部功能来执行此操作.我使用这种技术将png:s 十六进制编码为单个可执行文件.这需要对includetxt.go进行微小的更改.

  • 只是好奇,为什么我们必须在注释中编写预处理器命令然后单独运行`go generate`?当它注意到`// go:generate ...`comment时,不能"去构建"处理运行命令吗? (2认同)
  • 我是Go newpie,它通过运行主文件给了我未定义的a和b,includetext.go文件已成功生成。 (2认同)

jos*_*hlf 28

使用go-bindata.来自README:

此工具将任何文件转换为可管理的Go源代码.用于将二进制数据嵌入到go程序中.在转换为原始字节切片之前,文件数据可选地进行gzip压缩.

  • 现在这已经过时了.生成代码不传递golint,并且repo的所有者不与pull请求交互.我会对这个图书馆保持警惕 (2认同)
  • 一个名为[statik](https://github.com/rakyll/statik)的新项目似乎是这样做的,并且最近一直活跃起来. (2认同)

Sev*_*ate 12

正在寻找相同的东西并遇到esc: Embedding Static Assets in Go(截至 2014 年 11 月 19 日),作者Matt Jibson正在评估其他 3 个声称可以进行文件嵌入的流行软件包:

  1. 拉基尔/斯塔蒂克
  2. jteeuwen/go-bindata(以及新的官方go-bindata/go-bindata和另一个改进的kevinburke/go-bindata
  3. GeertJohan/go.rice

并解释为什么他最终想出了自己的包裹:

  1. 米吉布森/esc

因此,在简单地尝试所有这些(按此顺序)之后,我自然而然地选择了 Matt 的esc,因为它是唯一一个开箱即用且具有我所需功能(单个可执行文件中的 HTTPS 服务)的工具,即:

  1. 可以采用一些目录并以与http.FileSystem兼容的方式递归地将所有文件嵌入其中
  2. 可以选择禁用与本地文件系统一起使用以进行本地开发,而无需更改客户端代码
  3. 不会在后续运行中更改输出文件在文件更改时具有合理大小的差异
  4. 能够通过//go:generate而不是强迫您手动编写额外的 Go 代码来完成工作

第 2点对我来说很重要,而其他软件包由于某种原因或其他原因并没有那么好。

来自 esc 的自述文件:

esc 将文件嵌入到 go 程序中,并为它们提供 http.FileSystem 接口。

它在指定路径的命名目录下递归添加所有命名文件或文件。输出文件提供了一个 http.FileSystem 接口,对标准库之外的包具有零依赖性。


Int*_*net 5

您可以使用a string literal将文本定义为常量或变量.字符串文字是通过用反引号括起字符串来定义的.例如`string`.

例如:

package main

import "fmt"

func main() {
    const text = `
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit  
amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante 
hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet 
vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut 
libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, 
consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a 
semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. 
Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut 
convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis 
quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis 
parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae 
nisi at sem facilisis semper ac in est.
`

    fmt.Println(text)
}
Run Code Online (Sandbox Code Playgroud)


zuo*_*zuo 5

检查packr,它使用起来非常友好

package main

import (
  "net/http"

  "github.com/gobuffalo/packr"
)

func main() {
  box := packr.NewBox("./templates")

  http.Handle("/", http.FileServer(box))
  http.ListenAndServe(":3000", nil)
}
Run Code Online (Sandbox Code Playgroud)


Avi*_*nwa 5

使用go1.16,您可以开始使用embed,这是标准包,可帮助您将静态非 go 文件嵌入到二进制文件中

文档: https: //pkg.go.dev/embed
示例: https: //blog.carlmjohnson.net/post/2021/how-to-use-go-embed/

对于go < 1.16,您可以使用packr这是一个很棒的工具,您可以在https://github.com/gobuffalo/packr查看更多相关信息