Подтвердить что ты не робот

Как встроить файлы в двоичные файлы Golang?

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

4b9b3361

Ответ 1

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

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

Ответ 2

Так как Go 1.4, вы можете использовать go генерировать, если вам нужна большая гибкость.

Если у вас есть несколько текстовых файлов или текстовый файл может измениться, вам может не потребоваться жестко указать текстовый файл, а включить его во время компиляции.

Если у вас есть следующие файлы:

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 запускает script после go:generate. В этом случае он запускает go script, который считывает все текстовые файлы и выводит их как строковые литералы в новый файл. Я пропустил обработку ошибок для более короткого кода.

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 файлы в ваш exectutable:

$ 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 encode png: s в один исполняемый файл. Это требует незначительных изменений для includeetxt.go.

Ответ 3

Вы можете использовать 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)
}

Ответ 4

Искал то же самое и наткнулся на esc: встраивание статических ресурсов в 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, так как он был единственным, который работал из коробки с необходимой мне функциональностью, а именно:

  1. Может взять несколько каталогов и рекурсивно внедрить все файлы в них способом, совместимым с http.FileSystem
  2. .При желании можно отключить для использования с локальной файловой системой для локальной разработки без изменения кода клиента
  3. Не изменит выходной файл при последующих запусках, имеет различия в размерах при изменении файлов
  4. Способен выполнять работу через //go:generate вместо того, чтобы заставлять вас вручную вводить дополнительный код Go

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

От esc README:

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

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

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

проверьте упаковщик, его довольно удобно использовать

package main

import (
  "net/http"

  "github.com/gobuffalo/packr"
)

func main() {
  box := packr.NewBox("./templates")

  http.Handle("/", http.FileServer(box))
  http.ListenAndServe(":3000", nil)
}