Use //go:embed in Go 1.16
//go:embed
Go 1.16 introduced a new //go:embed
directive that allows you to include the contents of arbitrary files and directories in your Go application.
The basic idea of embedding is that by adding a special comment to your code, Go will know to include a file or files. The comment should look like //go:embed FILENAME(S)
and be followed by a variable of the type you want to embed: string
or []byte
for an individual file or embed.FS
for a group of files.
The go:embed directive understands Go file globs, so patterns like files/.html will also work (but not **/.html recursive globbing).
You can read the official docs embed · pkg.go.dev - https://pkg.go.dev/embed for a complete technical explanation, so here let’s take a look at some examples to see what’s possible.
Usage
A //go:embed
directive above a variable declaration specifies which files to embed, using one or more path.Match patterns.
There are some rules or limits:
Like other directives, there can be no spaces between //
and go:embed
. (No error will be reported, but the instruction will be ignored by the compiler)
The directive must immediately precede a line containing the declaration of a single variable. Only blank lines and ‘//’ line comments are permitted between the directive and the declaration.
The type of the variable can only be string
, []byte
or embed.FS
, even the aliases of these three types will not work.
The patterns are interpreted relative to the package directory containing the source file. The path separator is a forward slash, even on Windows systems.
The //go:embed
directive can be used with both exported and unexported variables, depending on whether the package wants to make the data available to other packages. It can only be used with global variables at package scope, not with local variables.
Patterns must not match files outside the package’s module, such as '.git/*’ or symbolic links. Matches for empty directories are ignored. After that, each pattern in a //go:embed
line must match at least one file or non-empty directory.
If any patterns are invalid or have invalid matches, the build will fail.
Embedding one file into a string:
1 | // main.go |
Run main.go.
1 | go run main.go |
Embedding one file into a slice of bytes
1 | // main.go |
Run main.go.
1 | go run main.go |
Embedded one or more files into a file system
The FS type enables embedding a tree of files, such as a directory of static web server content, as in the example follow.
1 | // main.go |
Run main.go.
1 | go run main.go |
The //go:embed
directive accepts multiple space-separated patterns for brevity, but it can also be repeated, to avoid very long lines when there are many patterns.
1 | // content holds our static web server content. |
The variable in the above example is almost equivalent to:
1 | // content is our static web server content. |
Patterns may not contain ‘.’ or ‘…’ or empty path elements, nor may they begin or end with a slash.
To match everything in the current directory, use ‘*’ instead of ‘.’. To allow for naming files with spaces in their names, patterns can be written as Go double-quoted or back-quoted string literals.
For a directory, the directory will be the root, and all files and subdirectories will be embedded recursively.
File Systems
FS implements the io/fs package’s FS interface, so it can be used with any package that understands file systems, including net/http, text/template, and html/template.
For example, given the content variable in the example above, we can write:
1 | // main.go |
Or use with html/template.
1 | template.ParseFS(content, "*.tmpl") |
Gotchas
There are some gotchas with embedding to be aware of.
Forget to import the embed package
First of all, you must import the embed package in any file that uses an embed directive. So a file like this won’t work:
1 | // missing-embed.go |
run missing-embed.go.
1 | go run missing-embed.go |
Use within functions or methods
Second, you can only use //go:embed for variables at the package level, not within functions or methods, so a program like this won’t compile:
1 | package main |
Run bad-level.go.
1 | go run bad-level.go |
Ignore some files by default
Third, when you include a directory, it won’t include files that start with . or _, but if you use a wildcard, like dir/*, it will include all files that match, even if they start with . or _.
Bear in mind that accidentally including Mac OS’s .DS_Store files may be a security problem in circumstances where you want to embed files in a webserver but not allow users to see a list of all the files.
For security reasons, Go also won’t follow symbolic links or go up a directory when embedding.
References
[1] embed · pkg.go.dev - https://pkg.go.dev/embed
[3] Go 1.16 is released - The Go Blog - https://blog.golang.org/go1.16
[4] The Go Programming Language - https://golang.org/
[5] 提前试用将在 Go1.16 中发布的内嵌静态资源功能 - https://polarisxu.studygolang.com/posts/go/dynamic/go-embed-try/