いろいろ備忘録日記

主に .NET とか Go とか Python絡みのメモを公開しています。

Goメモ-147 (embed メモ (複数ファイル, embed.FSで取得))

概要

Go 1.16 から追加となった embed パッケージについてのメモ4個目。

前回は以下。

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

embed パッケージのドキュメントは以下。

golang.org

今回は、複数のファイルを埋め込んで、その内容を embed.FS で取得するサンプルです。

embed.FS は、こちらも Go1.16 で追加された io/fs の 各インターフェースを embed 側で実装しているものです。

embed.FS は、以下のインターフェースを実装しています。

  • fs.ReadDirFS
  • fs.ReadFileFS

なので、ファイルとディレクトリの両方を扱えるってことですね。

embedパッケージで、複数のファイルを一つの変数で埋め込んで利用する場合は、embed.FS を利用する必要があります。

埋め込みたい変数に、必要なだけ //go:embed xxxx を書けば埋め込まれます。

例えば以下のようにします。

//go:embed data
//go:embed helloworld.txt
var content embed.FS

上記では、data というディレクトリと helloworld.txt というファイルを埋め込むよう指示しています。

サンプル

以下のサンプルでは、同一ディレクトリに配置されている helloworld.txtdata ディレクトリを埋め込んで利用しています。

helloworld.txt というファイルは以下の内容です。

hello
world

data ディレクトリの中は以下な感じ。

$ tree data
data
├── dotnet.txt
├── golang.txt
├── go.txt
└── python.txt

0 directories, 4 files

それぞれのファイルの中身は言語の名前が一行で入っているだけです。

んで、利用する側は以下のような感じ。

package embeds

import (
    "embed"
    "fmt"

    "github.com/devlights/gomy/output"
)

//go:embed data
//go:embed helloworld.txt
var content embed.FS

// EmbedFsMultifiles は、embed パッケージの機能を確認するサンプルです (embed.FSとして複数ファイルを操作)
func EmbedFsMultifiles() error {
    var (
        buf []byte
        err error
    )

    //
    // 埋め込まれたファイルの操作
    //
    buf, err = content.ReadFile("helloworld.txt")
    if err != nil {
        return err
    }

    output.Stdoutl("[helloworld.txt]", string(buf))
    output.StdoutHr()

    //
    // 埋め込まれたディレクトリの操作
    //
    entries, err := content.ReadDir("data")
    if err != nil {
        return err
    }

    for _, entry := range entries {
        if entry.IsDir() {
            continue
        }

        var (
            name = entry.Name()
            path = fmt.Sprintf("data/%s", name)
        )

        buf, err = content.ReadFile(path)
        if err != nil {
            return err
        }

        output.Stdoutl(fmt.Sprintf("[%s]", name), string(buf))
    }

    return nil
}

実行すると以下のようになります。

$ make run

go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: embed_fs_multifile
[Name] "embed_fs_multifiles"

[helloworld.txt]     hello
world
-------------------------------------------------- 
[dotnet.txt]         dotnet
[go.txt]             golang
[golang.txt]         golang
[python.txt]         python


[Elapsed] 261.159µs

ちゃんと動作していますね。実行時には当然 helloworld.txt も data ディレクトリも必要ありません。

注意点として、バイナリにはそのまま埋め込まれていますので、バイナリエディタとかで除くと当然丸見えです。センシティブなデータは埋め込まないようにしましょう。(しないと思いますが)

参考資料

golang.org


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com