Как вставлять файлы в бинарники Go

У меня есть текстовый файл, который я прочитал из своей программы Go. Я хотел бы отправить один исполняемый файл без дополнительного предоставления этого текстового файла. Как мне встроить его в компиляцию на Windows и Linux?


person Zvika    schedule 22.07.2013    source источник


Ответы (8)


Используйте go-bindata. Из README:

Этот инструмент преобразует любой файл в управляемый исходный код Go. Полезно для встраивания двоичных данных в программу go. Данные файла дополнительно сжимаются gzip перед преобразованием в необработанный байтовый фрагмент.

person joshlf    schedule 22.07.2013
comment
Сейчас это как-то устарело. Сгенерированный код не проходит golint, и владелец репозитория не взаимодействует с запросами на включение. я бы с опаской относился к этой библиотеке - person kio; 26.07.2016
comment
Похоже, этим занимается новый проект под названием statik, который активно работает в последнее время. - person Corey Ogburn; 01.06.2017
comment
Похоже, этот инструмент перенесен в новый специализированный проект — github.com/go-bindata/go- bindata, но есть и другие (и, возможно, лучшие) альтернативы, упомянутые в моем ответе. - person Sevenate; 07.09.2019

===== Редактировать янв 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.

person Johan Wikström    schedule 07.04.2015
comment
Хороший! Я понятия не имел, что функция существует. Это намного чище, чем выгружать строковый литерал в источнике. - person Nat Knight; 02.05.2015
comment
Просто любопытно, почему мы должны писать команду препроцессора в комментариях, а затем отдельно запускать go generate? Не может ли go build позаботиться о выполнении команды, когда заметит комментарий //go:generate ...? - person Bunyk; 12.12.2017
comment
По состоянию на 23 июля 2019 г. этот код не работает для текстовых файлов, содержащих обратные кавычки. Они будут либо производить синтаксические ошибки, либо допускать внедрение кода, что нежелательно. - person Roland Illig; 23.07.2019
comment
Я новичок в Go, он дал мне неопределенные a и b при запуске основного файла, файл includetext.go был успешно сгенерирован. - person Hasan A Yousef; 31.05.2020
comment
@Bunyk, я нашел это объяснение в статье Go Blog: последний выпуск Go, 1.4, включает новая команда, упрощающая запуск таких инструментов. Он называется go generate и работает, сканируя специальные комментарии в исходном коде Go, которые определяют общие команды для запуска. Важно понимать, что go generate не является частью go build. Он не содержит анализа зависимостей и должен запускаться явно перед запуском сборки go. Он предназначен для использования автором пакета Go, а не его клиентами. - person b01; 12.01.2021

Искал то же самое и наткнулся на esc: Embedding Static Assets in Go (от 19 ноября 2014 г.), где Автор, Мэтт Джибсон, оценивает 3 других популярных пакета, которые утверждают, что могут встраивать файлы:

  1. rakyll/statik
  2. jteeuwen/go-bindata (и новый официальный go-bindata/go-bindata и еще один улучшенный kevinburke/go-bindata)
  3. GeertJohan/go.rice

и объясните, почему он в конце концов придумал свой собственный пакет:

  1. mjibson/esc

Итак, попробовав их все (именно в таком порядке), я, естественно, остановился на esc Мэтта. был единственный, который работал из коробки с необходимым мне функционалом (служба HTTPS в одном исполняемом файле), а именно:

  1. Может взять несколько каталогов и рекурсивно встроить в них все файлы таким образом, чтобы это было совместимо с https://golang.org/src/net/http/fs.go?s=2511:2573#L77
  2. При желании можно отключить для использования с локальной файловой системой для локальной разработки без изменения кода клиента.
  3. Не будет изменяться выходной файл при последующих запусках, имеет различия разумного размера при изменении файлов.
  4. Возможность выполнять работу через //go:generate вместо того, чтобы заставлять вас вручную писать дополнительный код Go

Пункт #2 был для меня важен, а остальные пакеты по тем или иным причинам не сработали.

Из README esc:

esc встраивает файлы в программы go и предоставляет им интерфейсы http.FileSystem.

Он добавляет все именованные файлы или файлы рекурсивно в именованные каталоги по указанному пути. Выходной файл предоставляет интерфейс http.FileSystem с нулевой зависимостью от пакетов вне стандартной библиотеки.

person Sevenate    schedule 06.09.2019

Вы можете использовать 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 Intermernet    schedule 23.07.2013
comment
Хотя проще, это не работает, если ваш «файл» содержит такие символы, как %. - 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)
}
person zuo    schedule 06.10.2019
comment
Если вы смотрите на наибольшее количество звезд как на меру программного обеспечения, то это то, что вам нужно. - person Phani Rithvij; 17.10.2020
comment
Который будет заменен pkger. - person Sevenate; 26.01.2021

С 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

person Avinash Dhinwa    schedule 24.04.2021

Я использовал простую функцию, чтобы прочитать внешний шаблон в go generate прогоне и сгенерировать из него код Go. Будет сгенерирована функция, возвращающая шаблон в виде строки. Затем можно проанализировать возвращенную строку шаблона, используя tpl, err := template.New("myname").Parse(mynameTemplate())

Я разместил этот код на github. Вы можете попробовать https://github.com/wlbr/templify.

Очень просто, но работает для меня довольно хорошо.

person wlbr    schedule 28.05.2016

Основываясь на комментарии @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

должен создать бинарный файл, содержащий файлы

person 030    schedule 21.10.2018
comment
В настоящее время я бы рекомендовал packr, см. этот ответ. - person 030; 28.11.2020