web-dev-qa-db-ja.com

Golangバイナリにファイルを埋め込む方法は?

Goプログラムから読み込んだテキストファイルがあります。追加でテキストファイルを提供せずに、単一の実行可能ファイルを出荷したいと考えています。 WindowsとLinuxでコンパイルに組み込む方法を教えてください。

42
Zvika

go-bindata を使用します。 READMEから:

このツールは、任意のファイルを管理可能なGoソースコードに変換します。バイナリデータをgoプログラムに埋め込むのに便利です。ファイルデータは、生のバイトスライスに変換される前に、オプションでgzip圧縮されます。

28
joshlf

Go 1.4以降、より柔軟性が必要な場合は go generate を使用できます。

複数のテキストファイルがある場合、またはテキストファイルが変更される可能性がある場合は、テキストファイルをハードコーディングせずに、コンパイル時に含めます。

次のファイルがある場合:

main.go
scripts/includetxt.go
a.txt
b.txt

また、main.go内のすべての.txtファイルのコンテンツにアクセスしたい場合は、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スクリプトを実行します。短いコードではエラー処理をスキップしました。

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
hello
world

UTF8エンコードされたファイルをエンコードしている限り、これは問題なく動作します。他のファイルをエンコードしたい場合は、go言語(またはその他のツール)の能力をフルに活用してください。この手法を使用して hex encode png:sを1つの実行可能ファイルに変換しました。これには、includetxt.goを少し変更する必要があります。

48
Johan Wikström

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)
}
5
Intermernet

同じことを探していて、偶然出会った esc:静的アセットをGoに埋め込む (2014年11月19日までに)ここで、著者 Matt Jibson は、主張する他の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コードを手動で作成する代わりに、//go:generateを介して作業を行うことができます

ポイント#2は私にとって重要であり、残りのパッケージは何らかの理由でうまく機能しませんでした。

EscのREADMEから:

escはg​​oプログラムにファイルを埋め込み、それらにhttp.FileSystemインターフェイスを提供します。

指定されたパスにある名前付きディレクトリの下に、すべての名前付きファイルまたは再帰的にファイルを追加します。出力ファイルは、標準ライブラリの外部のパッケージへの依存性がゼロのhttp.FileSystemインターフェースを提供します。

4
Sevenate

単純な関数を使用して、_go generate_実行で外部テンプレートを読み取り、そこからGoコードを生成しました。テンプレートを文字列として返す関数が生成されます。次に、tpl, err := template.New("myname").Parse(mynameTemplate())を使用して、返されたテンプレート文字列を解析できます

私はそのコードをgithubに配置しました。あなたは試してみたいかもしれません https://github.com/wlbr/templify

とてもシンプルですが、私にとってはとてもうまくいきます。

2
wlbr

@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

ファイルを含むバイナリを作成する必要があります

2
030

チェック 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)
}
1
zuo