Goプログラムに静的リソースをバンドルする最良の方法は何ですか?[閉まっている]


100

私はGoで小さなWebアプリケーションに取り組んでいます。これは、開発者のマシンでアプリケーション/ Webサービスのデバッグを支援するツールとして使用することを目的としています。プログラムへのインターフェースは、HTMLだけでなく、JavaScript(機能用)、画像、CSS(スタイリング用)を含むWebページです。このアプリケーションをオープンソース化することを計画しているので、ユーザーはMakefileを実行でき、すべてのリソースが必要な場所に移動します。ただし、実行可能ファイルをできるだけ少ないファイル/依存関係で簡単に配布できるようにもしたいと考えています。HTML / CSS / JSを実行可能ファイルにバンドルするための良い方法はありますか?ユーザーは1つのファイルをダウンロードして心配するだけで済みますか?


現在、私のアプリでは、静的ファイルの提供は次のようになります。

// called via http.ListenAndServe
func switchboard(w http.ResponseWriter, r *http.Request) {

    // snipped dynamic routing...

    // look for static resource
    uri := r.URL.RequestURI()
    if fp, err := os.Open("static" + uri); err == nil {
        defer fp.Close()
        staticHandler(w, r, fp)
        return
    }

    // snipped blackhole route
}

したがって、それは非常に単純です。要求されたファイルが静的ディレクトリに存在する場合は、ハンドラーを呼び出します。ハンドラーは、単にファイルを開き、Content-Type提供する前に適切な設定を試みます。私の考えは、これが実際のファイルシステムに基づく必要がある理由はないということでした:コンパイルされたリソースがある場合、リクエストURIでそれらにインデックスを付けて、そのようにそれらを提供できます。

これを行う良い方法がない場合、またはこれを試みて間違ったツリーを吠えている場合は、お知らせください。私は、エンドユーザーが管理するファイルをできるだけ少なくしてほしいと考えていました。

より適切なタグがある場合 、それらを追加するか、私に知らせてください。



今日はまったく同じ質問について考えました。私が探求する可能性のある解決策はgo generate、ファイルを[]byteコードに変数として埋め込まれたスライスに変換するために、小さなコマンドラインユーティリティ(私のソースコードにパッケージされている)を使用する方法ですstringerblog.golang.orgを参照)。 / generate)。
ラルフ

回答:


76

go-bindataパッケージは、あなたが興味を持っているように見えます。

https://github.com/go-bindata/go-bindata

これにより、静的ファイルをコードに埋め込むことができる関数呼び出しに変換し、呼び出されたときにファイルコンテンツのバイトスライスを返すことができます。


8
私の場合、これを支持するのは奇妙に自己奉仕のようですが、とにかくそれを行います:pただし、記録のために、それはパッケージではなく、コマンドラインツールです。
2012

参考までに、これは私のプロジェクトで取った道です。ある時点で、@ jimtは物事をよりユーザーフレンドリーにするためのいくつかの新機能を導入しましたが、必要な粒度が提供されなくなったため、機能が少ないがユースケース用に設計された独自のツールを作成しました(このツールを一種のツールとして使用しています)ビルドプロセスの前文):github.com/jimmysawczuk/go-binary
Jimmy Sawczuk

37

テキストファイルの埋め込み

テキストファイルの場合は、ソースコード自体に簡単に埋め込むことができます。逆引用符を使用して、次stringのようにリテラルを宣言します。

const html = `
<html>
<body>Example embedded HTML content.</body>
</html>
`

// Sending it:
w.Write([]byte(html))  // w is an io.Writer

最適化のヒント:

ほとんどの場合、リソースをに書き込むだけでよいのでio.Writer[]byte変換の結果を保存することもできます。

var html = []byte(`
<html><body>Example...</body></html>
`)

// Sending it:
w.Write(html)  // w is an io.Writer

注意しなければならないことは、生の文字列リテラルに逆引用符( `)を含めることができないことです。生の文字列リテラルにはシーケンスを含めることはできません(解釈された文字列リテラルとは異なります)。したがって、埋め込むテキストに逆引用符が含まれている場合は、この例のように、生の文字列リテラルを分割し、逆引用符を解釈された文字列リテラルとして連結する必要があります。

var html = `<p>This is a back quote followed by a dot: ` + "`" + `.</p>`

これらの連結はコンパイラーによって実行されるため、パフォーマンスは影響を受けません。

バイナリファイルの埋め込み

バイトスライスとして保存する

バイナリファイル(画像など)の場合、(結果のネイティブバイナリに関して)最もコンパクトで最も効率的なのは、ファイルのコンテンツを[]byteソースコードのとして持つことです。これは、go-bindataなどのサードパーティのtoos / librariesによって生成できます。

これにサードパーティライブラリを使用したくない場合は、バイナリファイルを読み取り、ファイル[]byteの正確なコンテンツで初期化されるタイプの変数を宣言するGoソースコードを出力する簡単なコードスニペットを次に示します。

imgdata, err := ioutil.ReadFile("someimage.png")
if err != nil {
    panic(err)
}

fmt.Print("var imgdata = []byte{")
for i, v := range imgdata {
    if i > 0 {
        fmt.Print(", ")
    }
    fmt.Print(v)
}
fmt.Println("}")

ファイルに0〜16バイトが含まれる場合の出力例(Go Playgroundで試してください):

var imgdata = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

base64として保存 string

ファイルが「大きすぎない」(ほとんどの画像/アイコンが適格である)場合は、他にも実行可能なオプションがあります。ファイルのコンテンツをBase64に変換stringし、ソースコードに保存できます。アプリケーションの起動時(func init())または必要に応じて、元の[]byteコンテンツにデコードできます。Goは、encoding/base64パッケージでBase64エンコーディングをサポートしています。

(バイナリ)ファイルをbase64に変換するのstringは次のように簡単です:

data, err := ioutil.ReadFile("someimage.png")
if err != nil {
    panic(err)
}
fmt.Println(base64.StdEncoding.EncodeToString(data))

結果のbase64文字列をソースコードに、たとえばとして保存しますconst

デコードは関数呼び出しの1つにすぎません。

const imgBase64 = "<insert base64 string here>"

data, err := base64.StdEncoding.DecodeString(imgBase64) // data is of type []byte

引用通りに保​​管 string

base64として格納するよりも効率的ですが、ソースコードではバイナリデータの引用符付き文字列リテラルを格納する方が長くなる可能性があります。次のstrconv.Quote()関数を使用して、引用符付きの文字列を取得できます。

data, err := ioutil.ReadFile("someimage.png")
if err != nil {
    panic(err)
}
fmt.Println(strconv.Quote(string(data))

0から64までの値を含むバイナリデータの場合、出力は次のようになります(Go Playgroundで試してください)。

"\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?"

strconv.Quote()それに引用符を追加して付加することに注意してください。)

この引用符で囲まれた文字列をソースコードで直接使用できます。次に例を示します。

const imgdata = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?"

使用する準備ができており、デコードする必要はありません。引用解除は、コンパイル時にGoコンパイラによって行われます。

必要に応じて、バイトスライスとして保存することもできます。

var imgdata = []byte("\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?")

shファイルをgo実行可能ファイルにバインドする方法はありますか?
Kasun Siyambalapitiya 2017年

「バイトスライスとして保存する」セクションの最初のコードスニペットでは、データはimgdataであると思います。
論理x 2

1
@deusexmachinaそうです、修正しました。遊び場のコードはすでに正しいです。
icza

2

また、エキゾチックな方法もあります。Maven プラグインを使用してGoLangプロジェクトを構築し、JCPプリプロセッサを使用してバイナリブロックとテキストファイルをソースに埋め込むことができます。ケースコードは下の行のように見えます(そしていくつかの例はここにあります

var imageArray = []uint8{/*$binfile("./image.png","uint8[]")$*/}

@ sh上記のような実行可能ファイルを持つディレクトリをバインドすることは可能ですか
Kasun Siyambalapitiya

@KasunSiyambalapitiyaディレクトリをバインドしますか?shファイルをバインドしますか?よく分からない。ディレクトリ内のすべてを埋め込む必要がある場合、これは私がやったことgo-bindataです。たとえば//go:generate $GOPATH/bin/go-bindata -prefix=data/ -pkg=$GOPACKAGE data/、(生成されていない)goファイルをgo generate ./...挿入すると、パッケージのディレクトリでgo-bindataが実行され、すべてがデータサブディレクトリに埋め込まれますが、 'data /'プレフィックスは削除されます。
マーク

1

mjibson / escはgo-bindata、別の回答で言及されている一般的な代替手段として、任意のファイルも埋め込みますが、ディレクトリツリーを特に便利に処理します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.