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

时间:2013-07-22 19:42:07

标签: go binaries

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

8 个答案:

答案 0 :(得分:43)

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

如果您有多个文本文件或文本文件可能更改,您可能不想对文本文件进行硬编码,但在编译时将其包含在内。

如果您有以下文件:

main.go
scripts/includetxt.go
a.txt
b.txt

想要访问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)
}

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"))
}

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

$ go generate
$ go build -o main

现在你的目录结构如下:

main.go
main
scripts/includetxt.go
textfiles.go
a.txt
b.txt

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

生成

textfiles.go

package main 

const (
a = `hello`
b = `world`
)

运行main给出了

$ ./main
hello
world

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

答案 1 :(得分:28)

使用go-bindata。来自自述文件:

  

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

答案 2 :(得分:5)

您可以使用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)
}

答案 3 :(得分:3)

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

  1. rakyll/statik
  2. jteeuwen/go-bindata(以及新的官方go-bindata/go-bindata和另一个改进的kevinburke/go-bindata
  3. GeertJohan/go.rice

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

  1. mjibson/esc

因此,在短暂地尝试了所有顺序之后,我自然选择了马特(Matt)的esc,因为它是唯一对我有帮助的现成产品 功能,即:

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

#2 这点对我来说很重要,而其他软件包出于某种原因却无法很好地解决问题。

从esc的自述文件:

  

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

     

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

答案 4 :(得分:3)

使用 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

上查看更多信息

答案 5 :(得分:2)

我使用一个简单的函数来读取go generate运行中的外部模板,并从中生成Go代码。将生成将模板作为字符串返回的函数。然后,可以使用tpl, err := template.New("myname").Parse(mynameTemplate())

解析返回的模板字符串

我确实把代码放到了github上。您可能想尝试https://github.com/wlbr/templify

很简单,但对我很有用。

答案 6 :(得分:2)

基于@CoreyOgburn注释和此Github comment,创建了以下代码段:

//go:generate statik -src=./html

package main

import (
    _ "./statik"
    "github.com/rakyll/statik/fs"
)

func statikFile() {
    s, _ := fs.New()
    f, _ := s.Open("/tmpl/login.html")
    b, _ := ioutil.ReadAll(f)
    t, _ := template.New("login").Parse(string(b))
    t.Execute(w, nil)
}

然后运行

go generate

随后

go build

应创建一个包含文件的二进制文件

答案 7 :(得分:1)

检查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)
}