Help us understand the problem. What is going on with this article?

Golangで静的ファイルをバイナリに含めるライブラリを書いてみた

More than 3 years have passed since last update.

概要

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化した静的ファイルの内容が追記されます。

実行ファイル内でファイルを読み取るには、下記のようにします。

main.go
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など。)

サーバのソースは下記のようにしておきます。

web.go
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の内容をメモリに展開するのであまり大きなファイルには向きませんが、小さなファイルを実行ファイルに含めたい場合には便利に使えると思います。

srtkkou
Japanese engineer working in Osaka, Japan. Using Ruby, Java, C# and Go for software development. It is OK to contact me in English. 日本の大阪で働く日本人エンジニアです。Ruby、Java、C#、Goなどが使えます。英語での連絡OKです。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away