我有一些文本文件,我从我的 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
hello
world
只要您编码的是UTF8编码的文件,这将正常工作。如果您想编码其他文件,则可以利用go语言(或任何其他工具)的全部功能来实现。我使用此技术将png文件十六进制编码为单个可执行文件。这需要对includetxt.go进行微小更改。
使用 go-bindata 工具。从 README 文件中可以看到:
该工具可将任何文件转换为易于管理的 Go 源代码。它非常有用,可以将二进制数据嵌入到 Go 程序中。在转换为原始字节切片之前,文件数据可选择进行 gzip 压缩。
更新于2023年10月19日
来自之前推荐的作者mjibson/esc:
停止使用这个,现在切换到标准库中的embed。
原始答案
我也在寻找同样的东西,偶然发现了esc: 在Go中嵌入静态资源(作者为Matt Jibson),他正在评估其他三个声称可以进行文件嵌入的流行包。
并解释他最终为什么要开发自己的包:
所以在简短地尝试了它们之后(按照那个顺序),我自然而然地选择了Matt的esc,因为它是唯一一个能够直接使用(在一个可执行文件中)满足我需求的功能(HTTPS服务),具体包括://go:generate
执行工作,而无需手动编写额外的Go代码使用 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
字符串字面量
来将文本定义为常量或变量。字符串字面量通过用反引号括起来来定义字符串。例如:`字符串`。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)
}
根据@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
go generate
运行时读取外部模板,并从中生成 Go 代码。将生成返回模板字符串的函数。然后可以使用 tpl, err := template.New("myname").Parse(mynameTemplate())
解析返回的模板字符串。
go generate
?go build
不能在注意到//go:generate ...
注释时自动运行命令吗? - Bunykimport _ "embed"
,以便使用go:embed
指令将内容加载到string
或[]byte
类型的变量中。有关详细信息,请参见 github 上的讨论。 - ufukty