The embed package is a compiler command directive that allows a program to include all sorts of files and folders in the Go binary during compiling. The go:embed
method authorizes us to embed the contents of a file into a defined variable directly. This method allows Golang to read the file’s content while compiling. The embed package is only available in Go 1.16 and later. To check the Go version, we can use the following Go command:
go version
go:embed
work?The go:embed
compiler directive and the embed package, together, include static files in a Go binary. It is done by putting the //go:embed
comment above the declaration of the variable used for embedding.
//go:embed file_namevar fileName embed.FS
By this command, the compiler will search for a file with the specified name the file_name
, and include its assets in the resulting binary. The declared variable the fileName
after the go:embed
directive will act as a container for the contents embedded. The variable can be of different types such as the string
, []byte
, or embed.FS
, depending on how we want the embedded content to be handled. Through the following table, we can check which variable type should be used:
Variable Type | Access | Modifications | When to Use? | Example |
| The variable holds the embedded content as a string | In the Go application, resources embedded as | When a single file such as configuration data, a small template, or a piece of text needs to be embedded |
|
| The variable ends up as a slice of bytes containing the embedded content | The content of the | When individual files, especially binary files like images, fonts, or other non-text data, need to be embedded |
|
| The variable can be accessed as a part of the file system | In the Go application, resources embedded using the | When multiple files or an entire directory needs to be embedded as a read-only file system |
|
Here, we’ll embed contents of a text file into a []byte
. The file below is a text file named the test.txt
. In this text file, we have inserted a list of things, as mentioned below:
List of thingsAppleBananaTableChair
Here is the Golang program that we will use to embed the text file:
package mainimport (_ "embed""fmt")var (//go:embed test.txttext_file []byte)func main() {fmt.Println(string(text_file))}
Line 1: The package main
specifies that the Golang program has started, and the code written after this line is a part of the package main
.
Lines 2–5: This shows import of necessary packages. Here, the fmt
package is imported for printing, and the _ "embed"
. The _
before the "embed"
is to tell the Go compiler that the package is being imported for initializing embedded content, instead of directly using it in code.
Lines 6–9: A variable named the text_file
is declared which is of type []byte
. The comment //go:embed test.txt
is a command that instructs the Go compiler to embed contents of the test.txt
into the text_file
variable during compilation.
Lines 10–12: In the main
function, the fmt.Println()
function is used to print the content of the text_file
and string(text_file)
to convert byte slice to a string before printing it.
We can embed static files into a binary of a web application using the go:embed
method. Here is an HTML file named the index.html
that we will be embedding:
<!DOCTYPE html><html><head><title>Sample HTML Page</title></head><body><h1>Hello, World!</h1><p>Here is a sample static file embedded using Golang!</p></body></html>
The main.go
file that we will use to embed a static file is as follows:
package mainimport ("embed""fmt""log""net/http""os""strings")//go:embed publicvar static_files embed.FSvar staticDir = "public"func rootPath(h http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {w.Header().Set("Cache-Control", "no-cache")if r.URL.Path == "/" {r.URL.Path = fmt.Sprintf("/%s/", staticDir)}else {b := strings.Split(r.URL.Path, "/")[0]if b != staticDir {r.URL.Path = fmt.Sprintf("/%s%s", staticDir, r.URL.Path)}}h.ServeHTTP(w, r)})}func main() {var staticFS = http.FS(static_files)fs := rootPath(http.FileServer(staticFS))http.Handle("/", fs)port := os.Getenv("PORT")if port == "" {port = "3000"}log.Printf("Listening on :%s...\n", port)err := http.ListenAndServe(fmt.Sprintf(":%s", port), nil)if err != nil {log.Fatal(err)}}
Lines 10–11: The //go:embed
directive indicates that public
directory should be embedded to the static_files
variable using the embed.FS
type. It will be embedded during compilation and can be accessed via the static_files
.
Line 13: The staticDir
variable is declared with value public
, which is the directory that will be used to serve static files.
Line 15: The func rootPath(h http.Handler) http.Handler
introduces the rootPath()
function, which is middleware for processing incoming HTTP requests to ensure proper URL paths. It takes the http.Handler
as an argument and returns an http.Handler
.
Line 16: The return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request)
defines an anonymous function that takes w
(the http.ResponseWriter
) and r
(the http.Request
) as parameters.
Line 17: The w.Header().Set("Cache-Control", "no-cache")
sets the Cache-Control
header in the response to the "no-cache"
, which is used to prevent the caching of static files.
Lines 19–21: This block of code checks if the URL path is /
. If the URL path is /
, it will redirect the request by modifying the URL path to the "/{staticDir}/"
. This will effectively redirecting requests to the staticDir
route (e.g., from /
to /public/
).
Lines 22–23: The b := strings.Split(r.URL.Path, "/")[0]
is executed if the URL path is not /
. It checks if the first part of the URL path (before the first /
) is not equal to the staticDir
.
Lines 24–27: If the first part of the URL path is not equal to the staticDir
, it adds the staticDir
as a prefix to the URL path. This is done to ensure that requests are directed to the proper paths within the staticDir
.
Line 28: The modified request r
, along with response writer w
, is passed to the next HTTP handler h
for further processing.
Lines 29–30: The anonymous function is closed and the rootPath
middleware function is returned.
Lines 32–49: In the main()
function, the static_files
variable is converted into http.FS
type and used to create an http.FileServer
. The rootPath
middleware is applied to the file server for it to handle URL path modifications. The root path is handled by the fs
file server, and is set up to listen on the specified port in PORT
. If the PORT
environment variable is not set, it defaults to port 3000. The server is started using the http.ListenAndServe()
, and any errors are logged using the log.Fatal()
function.
Note: We can embed multiple static files in the same way.
To practice hosting embedded static files, press "Run" below and access the webpage on the given link.
<!DOCTYPE html> <html> <head> <title>Sample HTML Page</title> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <h1>Hello World</h1> <p>Here is a sample static file embedded using Golang!</p> </body> </html>
go:embed
We can find several advantages of the go:embed
in the following table:
Simplicity: The go:embed
simplifies embedding static assets, configuration files, and other resources into the Golang code.
Type safety: The go:embed
provides type safety, ensuring that the embedded files are of the expected type (e.g., string
, []byte
, or embed.FS
). This helps prevent runtime errors related to type mismatches.
Performance: Accessing embedded resources is often faster than reading files from disk, especially in scenarios where there are many small assets. This can improve the overall performance of the Go application.
Maintainability: Embedding resources in the Go code makes managing and versioning resources alongside source code easier. It simplifies the development workflow and reduces the risk of missing or mismatched assets.
go:embed
No dynamic embedding: The go:embed
requires specifying the files or directories to embed at compile-time. We cannot dynamically embed files based on runtime conditions.
Read-only resources: Embedded resources are typically read-only within the application. While this benefits security and consistency, the embedded resources cannot be modified at runtime.
File size: Embedding large files or directories can significantly increase the size of the Go binary, potentially impacting startup times and memory usage.
No encryption: Embedded resources are not automatically encrypted or obfuscated. If they contain sensitive data, additional steps need to be taken to secure them.
Despite the limitations, the go:embed
remains a convenient and resilient feature for embedding static files and resources in Go applications, especially for web development.
Free Resources