【发布时间】:2013-07-22 19:42:06
【问题描述】:
我有一些从 Go 程序中读取的文本文件。我想发布一个可执行文件,而不额外提供该文本文件。 如何将其嵌入到 Windows 和 Linux 上的编译中?
【问题讨论】:
我有一些从 Go 程序中读取的文本文件。我想发布一个可执行文件,而不额外提供该文本文件。 如何将其嵌入到 Windows 和 Linux 上的编译中?
【问题讨论】:
从 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 命令的特殊注释。
package main
import "fmt"
//go:generate go run scripts/includetxt.go
func main() {
fmt.Println(a)
fmt.Println(b)
}
go generate 命令将在go:generate 之后运行脚本。在这种情况下,它运行一个 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 由 go generate 和 script/includetxt.go 生成的地方
package main
const (
a = `hello`
b = `world`
)
运行 main 会给出
$ ./main
hello
world
只要您对 UTF8 编码的文件进行编码,它就可以正常工作。如果您想对其他文件进行编码,您可以使用 go 语言(或任何其他工具)的全部功能来执行此操作。我使用这种技术将hex encodepng:s 转换为单个可执行文件。这需要对 includetxt.go 稍作改动。
【讨论】:
go generate? go build 注意到 //go:generate ... 评论时不能处理运行命令吗?
使用go-bindata。来自自述文件:
此工具可将任何文件转换为可管理的 Go 源代码。对...有用 将二进制数据嵌入到 Go 程序中。文件数据是可选的 gzip 在转换为原始字节切片之前进行压缩。
【讨论】:
一直在寻找相同的东西并遇到了esc: Embedding Static Assets in Go(截至 2014 年 11 月 19 日),作者Matt Jibson 正在评估其他 3 个声称可以进行文件嵌入的流行包:
并解释为什么他最终想出了自己的包:
所以在简单地尝试了所有这些(按顺序)之后,我自然而然地选择了 Matt 的 esc,因为它是唯一一个开箱即用的对我来说是必需的功能( HTTPS 服务在单个可执行文件中),即:
//go:generate 完成工作,而不是强迫您手动编写额外的 Go 代码#2 点对我来说很重要,但由于某种原因,其余的软件包效果不佳。
来自 esc 的自述文件:
esc 将文件嵌入到 go 程序中,并为它们提供 http.FileSystem 接口。
它在指定路径的命名目录下递归添加所有命名文件或文件。输出文件提供了一个 http.FileSystem 接口,对标准库之外的包的依赖为零。
【讨论】:
您可以使用string literal 将文本定义为常量或变量。字符串文字是通过用反引号括起来的字符串来定义的。例如`字符串`。
例如:
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)
}
【讨论】:
% 之类的字符,则它不起作用。
使用 go1.16,您可以开始使用 embed,这是一个标准包,可帮助您将静态非 go 文件嵌入到二进制文件中
文档:https://pkg.go.dev/embed
示例:https://blog.carlmjohnson.net/post/2021/how-to-use-go-embed/
对于 go ,您可以使用 packr。这是一个很棒的工具,你可以在https://github.com/gobuffalo/packr查看更多信息
【讨论】:
我使用了一个简单的函数来读取go generate 运行中的外部模板并从中生成 Go 代码。将生成一个将模板作为字符串返回的函数。然后可以使用tpl, err := template.New("myname").Parse(mynameTemplate())
我确实将该代码放到了 github 上。你可能想试试https://github.com/wlbr/templify
非常简单,但对我来说效果很好。
【讨论】:
基于@CoreyOgburn 评论和Github comment,创建了以下sn-p:
//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
应该创建一个包含文件的二进制文件
【讨论】: