[訳]go実行可能ファイルにデータを埋め込む

6508 ワード

もとのアドレスhttps://github.com/XanthusL/blog-gen
テキストアドレスhttps://scene-si.org/2017/08/22/embedding-data-in-go-executables/
もしあなたが私にしばらく注目していたら、私がPendulumエディタを毎日少なくとも1時間符号化する100 DaysOfCodeとして開発していることを知っているはずです.Pendulumは、簡単なテキストとmarkdownファイルを編集するのに非常に適したwebベースのエディタです.
実はこの文章はそれを使って書いたのです.goバックエンドとVueJSフロントエンドで構成されています.すべてを含む単一の実行可能ファイルを簡単に使用し、提供することを望んでいます.そのため、ユーザーはインストーラをダウンロードしてファイルを解凍する必要はありません.すべてのものを一緒に梱包できる案を探しています.go-bindataでコード生成ですべてのデータをgo buildで実行可能ファイルに追加することにしました.

コード生成?


もちろんこれは簡単です.例えばgo-bindataツールはpublic_htmlディレクトリから対応するディレクトリを生成するのに役立つ.goファイル.これは私の応用シーンにとって素晴らしいです.しかし、なぜbashスクリプトやmakefileで生成するのでしょうか.go buildを実行する前にgo generateを実行するだけでgoのコード生成ツールを利用する必要があります.コード生成に慣れていない場合は、コードのどこかに簡単な注釈を加えるだけで、main.goで例を挙げます.
package main

//go:generate echo "Hello world"

func main() {
}
go generateを実行すると、「Hello world」が出力されていることがわかります.これはあなたがgo generateでコードを生成する実際のニーズではありません.//go:generateの後に書いたことはすべて実行されます.あなたが望むなら、go buildを実行することもできます.
package main

//go:generate echo "Hello world"
//go:generate go run main.go

func main() {
    println("Hello world from Go")
}

これを実行すると、予想される出力が得られます.
%go generate
Hello world
Hello world from Go

旗を掲げて勝つ!go generateは面白いです.Nodeプログラムは、babelによって、Node ES 5の実行時にES 6/ES 7の構文を互換化する.人々は似たような方法でgoに言語の現在の機能を超えた特性を提供することを試みている.
たとえばgennyは主に強いタイプのコードの生成に対応するため、手動でコピーして貼り付ける必要はありません.しかし、HaveのようなプロジェクトはBabelのノードに対する処理に近い.goに変換する言語を提供する.今のところ、この面でもっと魅力的な他の試みはまだ分かりません.しかし、Go 2や汎用型についての議論は面白いようだ.
これは私たちのアプリケーションシーンにとって少し退屈で、私たちはただいくつかのデータをプログラムにパッケージします.そんな雑談はさておき,本は本題に戻る.
//go:generate go-bindata -prefix front/src -o assets/bindata.go -pkg assets -nomemcopy front/src/dist/...

この行は少し長いので、分割してみましょう.
  • //go:generate-go generateヒント
  • go-bindata-実行するメインコマンド
  • -prefix front/src-「front/src」パッケージ
  • を除外
  • -o assets/bindata.go-指定出力ファイル
  • -pkg assets-生成するパッケージ名
  • -nomemcopy-メモリ使用量の最適化
  • front/src/dist/...-梱包する場所
  • これにより、app/assetsで簡単にインポートできるassetsパッケージがアプリケーションディレクトリの下に作成され、appがアプリケーションディレクトリに対応します.

    HTTPによる埋め込みファイルサービス


    これはちょっと複雑です.しかし、ドキュメントを見てみると簡単です.ローカルファイルに基づいてサービスを提供する場合は、次のようなコードが必要です.
    folder := http.Dir("/")
    server := http.FileServer(folder)
    http.Handle("/", server)

    実際、go-bindata-assetfsパッケージはhttpを提供している.FileServer実装.これは簡単に使えます.
    import "github.com/elazarl/go-bindata-assetfs"
    import "app/assets"
    // ...
    func main() {
        // ...
        files := assetfs.AssetFS{
            Asset:     assets.Asset,
            AssetDir:  assets.AssetDir,
            AssetInfo: assets.AssetInfo,
            Prefix:    "dist",
        }
        server := http.FileServer(&files)
        // ...
    }

    もう一つの小さな問題があります.pushHistoryを有効にしたVueJSアプリを使っています.これは、ユーザが使用すると、/blog/about.mdのような一般的なリンクが、釈伴符(ハッシュ,#)なしで表示されることを意味する.これらの適用処理が必要なリンクコンテンツはassetには存在しない.
    この問題も解決しにくいわけではない.assetfs.AssetFS構造体には、AssetsInfo法(os.Statに相当)とAsset法(ioutil.ReadFileに少し似ている)がある.これにより、あるファイルがassetに存在するかどうかを確認し、存在しない場合は別のファイルを出力することが可能になります.
    // Serves index.html in case the requested file isn't found
    // (or some other os.Stat error)
    func serveIndex(serve http.Handler, fs assetfs.AssetFS) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            _, err := fs.AssetInfo(path.Join(fs.Prefix, r.URL.Path))
            if err != nil {
                contents, err := fs.Asset(path.Join(fs.Prefix, "index.html"))
                if err != nil {
                    http.Error(w, err.Error(), http.StatusNotFound)
                    return
                }
                w.Header().Set("Content-Type", "text/html")
                w.Write(contents)
                return
            }
            serve.ServeHTTP(w, r)
        }
    }

    ファイルが見つかったら、自分のインプリメンテーションの代わりにプリセットのサービスHTTPメソッドを使用します.この方法では、以前定義したhandlerを少し調整するだけです.
    http.HandleFunc("/", serveIndex(server, assets))
    serveIndex関数は、対応する修正であるhttp.HandlerFuncを返します.これにより、go generateとgo-bindataでアプリケーションに追加したデータサービスの完全な実装が提供されます.//go:generateのコーナーをスキップしてCIスクリプトに入れてもいいです.
    これに鑑みてPendulumの単一実行可能なリリースを実現しました.GitHubパブリケーションページから入手して試してみることができます.
    編集:serveIndexの改善例感謝@Rdihipone

    ここを見たら...


    もしあなたが私の本を買うことができたら、きっと素晴らしいです.
  • API Foundations in Go
  • 12 Factor Apps with Docker and Go
  • The SaaS Handbook (work in progress)

  • I promise you’ll learn a lot more if you buy one. Buying a copy supports me writing more about similar topics. Say thank you and buy my books. Feel free to send me an email if you want to book my time for consultancy/freelance services. I’m great at APIs, Go, Docker, VueJS and scaling services, among many other things.