У меня есть текстовый файл, который я прочитал из своей программы Go. Я хотел бы отправить один исполняемый файл без дополнительного предоставления этого текстового файла. Как мне встроить его в компиляцию на Windows и Linux?
Как вставлять файлы в бинарники Go
Ответы (8)
Используйте go-bindata. Из README:
Этот инструмент преобразует любой файл в управляемый исходный код Go. Полезно для встраивания двоичных данных в программу go. Данные файла дополнительно сжимаются gzip перед преобразованием в необработанный байтовый фрагмент.
===== Редактировать янв 2021 =====
Начиная с Go 1.16, выпущенного в феврале 2021 г., вы можете использовать директиву 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
И хотите иметь доступ к содержимому всех файлов .txt в main.go, вы можете включить специальный комментарий, содержащий команду 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 в исполняемый файл:
$ 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-кодирования png:s в один исполняемый файл. Это требует незначительного изменения includetxt.go.
go generate
? Не может ли go build
позаботиться о выполнении команды, когда заметит комментарий //go:generate ...
?
- person Bunyk; 12.12.2017
Искал то же самое и наткнулся на esc: Embedding Static Assets in Go (от 19 ноября 2014 г.), где Автор, Мэтт Джибсон, оценивает 3 других популярных пакета, которые утверждают, что могут встраивать файлы:
- rakyll/statik
- jteeuwen/go-bindata (и новый официальный go-bindata/go-bindata и еще один улучшенный kevinburke/go-bindata)
- GeertJohan/go.rice
и объясните, почему он в конце концов придумал свой собственный пакет:
Итак, попробовав их все (именно в таком порядке), я, естественно, остановился на esc Мэтта. был единственный, который работал из коробки с необходимым мне функционалом (служба HTTPS в одном исполняемом файле), а именно:
- Может взять несколько каталогов и рекурсивно встроить в них все файлы таким образом, чтобы это было совместимо с https://golang.org/src/net/http/fs.go?s=2511:2573#L77
- При желании можно отключить для использования с локальной файловой системой для локальной разработки без изменения кода клиента.
- Не будет изменяться выходной файл при последующих запусках, имеет различия разумного размера при изменении файлов.
- Возможность выполнять работу через
//go:generate
вместо того, чтобы заставлять вас вручную писать дополнительный код Go
Пункт #2 был для меня важен, а остальные пакеты по тем или иным причинам не сработали.
Из README 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)
}
%
.
- person Sridhar Ratnakumar; 05.03.2015
проверьте 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)
}
С 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 а>
Я использовал простую функцию, чтобы прочитать внешний шаблон в go generate
прогоне и сгенерировать из него код Go. Будет сгенерирована функция, возвращающая шаблон в виде строки. Затем можно проанализировать возвращенную строку шаблона, используя tpl, err := template.New("myname").Parse(mynameTemplate())
Я разместил этот код на github. Вы можете попробовать https://github.com/wlbr/templify.
Очень просто, но работает для меня довольно хорошо.
Основываясь на комментарии @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
должен создать бинарный файл, содержащий файлы