Resource embedding tools like
embed allow developers to include static assets
and other unpublished files like templates in their executable programs.
Let’s see how.
Why would I use a bundler like
One of the reasons why Go is so popular is the ease of deployment of its applications: just copy the compiled binary and you’re done1.
However, as soon as one needs to copy a bunch of non-code files like CSS, JS, or Go templates, that advantages disappears unless one finds a way to embed them in the executable.
Facing the multiplicity of such solutions, and their varying levels of
efficiency and ease of use, the Go project eventually chose to include such
a feature within the language as its standard library, as the
and the embed package.
In an earlier article, we saw how to
bundle templates with pkger.
embed mechanism is the conclusion of ticket
already mentioned in that article, which is still relevant regarding the
motivations for embedding mechanisms.
Let us therefore see how to use
embed instead of
markbates/pkger for the
exact same example used in the previous article, to illustrate how to convert
from one approach to the other one.
How can I use
embed in my projects ?
pkger and various other embedding packages:
- the new mechanism requires no external dependency, just depending on Go 1.16 or newer
- there is no difference between a development and a production build, at least when it comes to assets embedding
- no API is needed to load a single resource like a template file
- the API to load a resource tree is made of only 3 methods
Once compiled, the resulting project has no remaining runtime dependency on pkger or the original resources.
embed to include resources in the bundle (executable, library, plugin),
the module sources must be adapted. Let us see how.
$ GO111MODULE=off go get github.com/fgm/pkger_demo $ cd $GOPATH/src/github.com/fgm/pkger_demo/v2
Loading an isolated resource
All it takes to load an isolated resource (a single file) with
byte variable in package scope,
labeling it with the
To load resources from an embedded resource tree, and load files from it selectively,
the embedding directive remains unchanged, e.g.
but the variable type must be
This new type was introduced in Go 1.16 and implements 3 new interfaces belonging
to the new
io/fs package of the standard library,
as a result of the file I/O rationalization in that version.
|X||X||via ||via |
This API is simpler than the one provided by earlier contributed packages,
as it allows using standard library functions to access resources listed in a
These embedding directives may include a
* wildcard to embed multiple files,
with just one pattern, and there can be multiple names or patterns,
either space-separated on a single directive line,
or on adjacent
//go:embed lines, with the latter being more readable and
easier to maintain over time in version control.
All files thus listed in a single
//go:embed block will be automatically
loaded in the
embed.FS variable, under their respective relative path.
Loading templates from a directory subtree
The pkger_demo V2 demo program is a basic Web application, which returns a page formatted by two templates, as is common practice.
Let us look into the
main.go file salient bits. Lines 1-13 contain the program
import), importing the
embed package referenced further down.
Nothing interesting there.
Lines 14-16 show how to embed an isolated resource as a
While lines 18-20 show how to embed multiple resources as a subtree,
automatically loaded in an
The program loads templates it finds in the
(the path is as provided in the
but also in all its subdirectories.
To discover them, it will have to crawl the substree starting at
and parse all discovered templates:
- line 33: this works much like
WalkDirfunction walks the subtree, invoking the
walkFncallback on each resource met.
In this example, the callback is the anonymous function at lines 33-46.
- lines 34-37: the function ignores non-files, and files not matching the expected template name format.
- lige 40, it opens the file using
templates.Open, getting a standard
fs.File, which happens to be an
- line 42, since
io.Readerthe function reads its content using the standard
io.ReadAllfunction. These two calls on lines 40-42 could also be replaced by a single
- ligne 44, with the result being a plain string, the function can now parse it as a Go template.
The final result of the function is a valid set of compiled templates, or an error value.
Once the program obtains the compiled templates at line 57, the HTTP request handlers can use the template without any notion of their loading mechanism:
- Official documentation: https://pkg.go.dev/embed
- Introductory blog post by Carl Johnson, recommended by the Go blog in the Go 1.6 announcement.
- Introductory video by Russ Cox exposing the design of the
yes, there are some limitations regarding shared networks libraries, but they are not relevant in this context. ↩︎