概要
Go言語の大きな魅力の一つが「シングルバイナリ」だと思います。実行ファイルが他のファイルに依存せずに単体で完結するので、デプロイがファイルのコピーで済み、非常に楽です。
しかしWebアプリケーションの場合、そうはいかないというのが個人的に大きな誤算でした。静的ファイルが実行ファイルに含まれるものと思っていたらそんなことはなく、実行時にも静的ファイルを置いておく必要があります。
有名なライブラリにgo-bindataというものがありまして、それを使えば実行ファイルに静的ファイルを含めることはできます。ですが、go-bindataは静的ファイルをgo言語のソースコードに翻訳するもので、バージョン管理システム上の管理が難しく、個人的に好みではありませんでした。
それを解決するためにzgokというライブラリを書いてみました。
Windows/Linuxの実行ファイルは、ファイルのヘッダで実行ファイルそのもののサイズの情報を持っています。なので実行ファイルの末尾に何かデータを付けても、実行ファイルの実行には支障ありません。なので、静的ファイルをZIP化したものと、それらのサイズを記述したフッター(シグネチャ)を追記しています。参考
※同じようなアイデアのライブラリはありましたが、Windows上でzipのインストールが必要だったりするのがいまいちなので自作しました。
インストール手順
インストールは下記のコマンドを実行してください。
go get github.com/srtkkou/zgok/...
$GOPATH/binに実行ファイルzgokが作成されます。
使用方法
zgok形式のファイルを作成するには、下記のコマンドを実行します。
$GOPATH/bin/zgok build -e 実行ファイル名 -z ZIPしたいパス1 -z ZIPしたいパス2 -o 出力ファイル名
静的ファイルはいくつでも指定することができます。実行ファイルの内容にZIP化した静的ファイルの内容が追記されます。
実行ファイル内でファイルを読み取るには、下記のようにします。
package main
import (
"fmt"
"github.com/srtkkou/zgok"
"io/ioutil"
"os"
)
func main() {
var content []byte
path := "test.txt"
zfs, _ := zgok.RestoreFileSystem(os.Args[0])
if zfs != nil {
// 本番環境
content, _ = zfs.ReadFile(path)
} else {
// 開発環境
content, _ = ioutil.ReadFile(path)
}
fmt.Println(string(content))
}
zfsが展開できたかどうかで分岐しておくと、開発環境では実ファイル、本番環境ではZgokファイルが使えて便利です。
そもそもの目的であるWebアプリの静的ファイルの取得のために、さらにnet/httpのFileServerの機能を取り込んでみました。例えば./web/public配下に静的ファイルがあるとします。(./web/public/css/sample.cssなど。)
サーバのソースは下記のようにしておきます。
package main
import (
"net/http"
"github.com/srtkkou/zgok"
"os"
)
func main() {
zfs, err := zgok.RestoreFileSystem(os.Args[0])
if err != nil {
panic(err)
}
assetServer := zfs.FileServer("web/public")
http.Handle("/assets/", http.StripPrefix("/assets/", assetServer))
http.ListenAndServe(":8080", nil)
}
さらに下記コマンドでビルドします。
go build -o web web.go
$GOPATH/bin/zgok build -e web -z web/public -o web_all
これでweb_allを実行し、ブラウザから静的ファイルのURLにアクセスすると、その内容が読み取れます。(http://localhost:8080/assets/css/sample.css)
この実行ファイル単体で、静的ファイルにもアクセスできました!
ZIPの内容をメモリに展開するのであまり大きなファイルには向きませんが、小さなファイルを実行ファイルに含めたい場合には便利に使えると思います。