如何将文件嵌入Go二进制文件

clj7thdc  于 2023-11-14  发布在  Go
关注(0)|答案(8)|浏览(147)

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

bwntbbo3

bwntbbo31#

=编辑2021年1月=

从2021年2月发布的Go 1.16开始,您可以使用go:embed指令:

import "embed"

//go:embed hello.txt
var s string
print(s)

//go:embed hello.txt
var b []byte
print(string(b))

//go:embed hello.txt
var f embed.FS
data, _ := f.ReadFile("hello.txt")
print(string(data))

字符串

=原始答案==

从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脚本,读取所有文本文件并将它们作为字符串输出到一个新文件中。我跳过了错误处理,以缩短代码。

script/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文件编译为可执行文件,请执行以下操作:

$ go generate
$ go build -o main


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

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


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

textfiles.go

package main 

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


运行主程序

$ ./main
hello
world


只要你编码的是UTF8编码的文件,这就可以正常工作。如果你想编码其他文件,你可以使用go语言(或任何其他工具)来完成。我使用这种技术将hex encode png:s转换为一个可执行文件。这需要对includetxt. go进行微小的修改。

6kkfgxo0

6kkfgxo02#

使用go-bindata。从自述文件:
这个工具将任何文件转换成可管理的Go源代码。用于将二进制数据嵌入到Go程序中。文件数据在转换成原始字节切片之前可以选择gzip压缩。

lsmepo6l

lsmepo6l3#

更新时间:2023-10-19

从作者以前推荐的原始答案mjibson/esc
停止使用这个并切换到标准库中的embed

原始答案

正在寻找同样的东西,遇到了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,因为它是唯一一个开箱即用的 * 对我来说是必要的 * 功能(在单个可执行文件中的HTTPS服务),即:
    1.可以使用一些目录并以与http.FileSystem兼容的方式递归地将所有文件嵌入其中
    1.可以选择禁用,以便与本地文件系统一起使用,进行本地开发,而无需更改客户端的代码
    1.在后续运行中不会更改输出文件,文件更改时差异大小合理
    1.能够通过//go:generate完成工作,而不是强迫您手动编写额外的Go代码
    第2点对我来说很重要,而其他的包由于这样或那样的原因并没有很好地工作。
    来自ESC的README:
    esc将文件嵌入到go程序中,并为它们提供http.FileSystem接口。
    它将所有命名文件或文件递归地添加到指定路径的命名目录下。输出文件提供了一个http.FileSystem接口,对标准库之外的包没有依赖性。
mu0hgdu0

mu0hgdu04#

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

字符串

oknrviil

oknrviil5#

使用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上查看更多关于它的信息

rslzwgfq

rslzwgfq6#

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

字符串

ua4mk5z4

ua4mk5z47#

我使用一个简单的函数在go generate运行中读取外部模板并从中生成Go代码。将生成一个以字符串形式返回模板的函数。然后可以使用tpl, err := template.New("myname").Parse(mynameTemplate())解析返回的模板字符串
我把代码放到github上了,你可以试试https://github.com/wlbr/templify
非常简单,但对我来说效果很好。

ruarlubt

ruarlubt8#

基于@CoreyOgburn评论和这个Github评论,创建了以下片段:

//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


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

相关问题