We are going to use embed directive initialises a variable of type string, byte or FS with the contents of files read from the package directory or subdirectories at compile time. This helps us including static files in the compiled binary and ship it.


Examples


Embed single file


├── .env
└── main.go

.env

ENV=dev
LOG=true
VER=0.1.0

main.go

package main

import (
"fmt"

_ "embed"
)

//go:embed .env
var envFile []byte

func main() {
fmt.Println(string(envFile))
}

Test

$ go run -race main.go
ENV=dev
LOG=true
VER=0.1.0

$ go build -o bin/main main.go
$ bin/main
ENV=dev
LOG=true
VER=0.1.0

Mapping all files in a folder to a struct


├── main.go
└── users
├── robert.json
└── tony.json

robert.json

{
"id": 1,
"name":"Robert"
}

tony.json

{
"id": 2,
"name": "Tony"
}

main.go

package main

import (
"embed"
"encoding/json"
"fmt"
"log"
)

type user struct {
ID int `json:"id"`
Name string `json:"name"`
}

//go:embed users
var usersDir embed.FS

func main() {
files, err := usersDir.ReadDir("users")
if err != nil {
log.Fatalln(err)
}

for _, file := range files {
val, err := usersDir.ReadFile("users/" + file.Name())
if err != nil {
fmt.Println(err)
continue
}

var usr user
if err := json.Unmarshal(val, &usr); err != nil {
fmt.Println(err)
continue
}

fmt.Printf("%+v\n", usr)
}
}

Test

$ go run -race main.go
{ID:1 Name:Robert}
{ID:2 Name:Robert}

$ go build -o bin/main main.go
$ bin/main
{ID:1 Name:Robert}
{ID:2 Name:Tony}

Reading only specific files in a folder


├── files
│   ├── 1.txt
│   ├── 2.txt
│   ├── 3.txt
│   └── sub
│   ├── 1.txt
│   └── 2.txt
└── main.go

1.txt

parent one

2.txt

parent two

3.txt

parent three

sub/1.txt

sub one

sub/2.txt

sub two

main.go

package main

import (
"embed"
"fmt"
)

//go:embed files
var filesDir embed.FS
var files = map[string]string{
"parent_one": "files/1.txt",
"parent_three": "files/3.txt",
"sub_one": "files/sub/1.txt",
}

func main() {
for name, path := range files {
val, err := filesDir.ReadFile(path)
if err != nil {
fmt.Println(err)
continue
}

fmt.Print(name, " > ", string(val))
}
}

Test

$ go run -race main.go
sub_one > sub one
parent_one > parent one
parent_three > parent three

$ go build -o bin/main main.go
$ bin/main
parent_one > parent one
parent_three > parent three
sub_one > sub one

Mapping files at compile time


├── main.go
├── multi.json
└── single.json

single.json

{
"name": "inanzzz"
}

multi.json

[
{
"id": 1
},
{
"id": 2
}
]

main.go

package main

import (
"encoding/json"
"fmt"

_ "embed"
)

func main() {
fmt.Printf("%+v\n", singleFunc)
fmt.Printf("%+v\n", multiFunc)
}

//go:embed single.json
var singleFile []byte
var singleFunc = func() (single struct {
Name string `json:"name"`
}) {
if err := json.Unmarshal(singleFile, &single); err != nil {
panic(err)
}
return
}()

//go:embed multi.json
var multiFile []byte
var multiFunc = func() (multi []struct {
ID int `json:"id"`
}) {
if err := json.Unmarshal(multiFile, &multi); err != nil {
panic(err)
}
return
}()

Test

$ go run -race main.go
{Name:inanzzz}
[{ID:1} {ID:2}]

$ go build -o bin/main main.go
$ bin/main
{Name:inanzzz}
[{ID:1} {ID:2}]