How to embed files into Go binaries

Zvika picture Zvika · Jul 22, 2013 · Viewed 30.1k times · Source

I have some text file that I read from my Go program. I'd like to ship a single executable, without supplying that text file additionally. How do I embed it into compilation on Windows and Linux?

Answer

Johan Wikström picture Johan Wikström · Apr 7, 2015

===== Edit Jan 2021 =====

Starting with Go 1.16, released in Feb 2021, you can use the go:embed directive:

import "embed"

//go:embed hello.txt
var s string
print(s)

//go:embed hello.txt
var b []byte
print(string(b))

//go:embed hello.txt
var f embed.FS
data, _ := f.ReadFile("hello.txt")
print(string(data))

===== Original answer ======

Since Go 1.4, you can use go generate if you need more flexibility.

If you have more than one text file or the text file may change you might not want to hardcode the text file but include it at compile time.

If you have the following files:

main.go
scripts/includetxt.go
a.txt
b.txt

And want to have access to the contents of all .txt files in main.go, you can include a special comment containing a go generate command.

main.go

package main

import "fmt"

//go:generate go run scripts/includetxt.go

func main() {
    fmt.Println(a)
    fmt.Println(b)
}

The go generate command will run the script after go:generate. In this case it runs a go script which reads all text files and outputs them as string literals into a new file. I skipped the error handling for shorter code.

script/includetxt.go

package main

import (
    "io"
    "io/ioutil"
    "os"
    "strings"
)

// Reads all .txt files in the current folder
// and encodes them as strings literals in textfiles.go
func main() {
    fs, _ := ioutil.ReadDir(".")
    out, _ := os.Create("textfiles.go")
    out.Write([]byte("package main \n\nconst (\n"))
    for _, f := range fs {
        if strings.HasSuffix(f.Name(), ".txt") {
            out.Write([]byte(strings.TrimSuffix(f.Name(), ".txt") + " = `"))
            f, _ := os.Open(f.Name())
            io.Copy(out, f)
            out.Write([]byte("`\n"))
        }
    }
    out.Write([]byte(")\n"))
}

To compile all .txt files into your exectutable:

$ go generate
$ go build -o main

Now your directory structure will look like:

main.go
main
scripts/includetxt.go
textfiles.go
a.txt
b.txt

Where textfiles.go was generated by go generate and script/includetxt.go

textfiles.go

package main 

const (
a = `hello`
b = `world`
)

And running main gives

$ ./main
hello
world

This will work fine as long as you're encoding UTF8 encoded files. If you want to encode other files you have the full power of the go language (or any other tool) to do so. I used this technique to hex encode png:s into a single executable. That requires a minor change to includetxt.go.