
If you're building a web app with Mux and need to serve static files from a folder, but don't want to use the internal file system, you can embed those files in your binary with Go Embed. This makes it easier to distribute your app and serve files from a single binary, rather than to link out to resources.
Go Embed is a feature that allows you to embed static files directly into your Go code, making them easily accessible without the need for an external file system. This is especially useful if you want to package your code and distribute it as a single binary. It's also great for serving static files to clients, since you can embed them directly into the executable and serve them over HTTP.
How do I access a single file?
To get started with Go Embed, you'll first need to define a variable that will hold the contents of the file you want to embed. For example, if you want to embed a file called index.html
in a sub-folder called assets
which has a basic <p>hello world!</p>
contained within, you can define a variable like this:
//go:embed assets/index.html
var index string
func main() {
fmt.Println(index)
}
// output: <p>hello world</p>
Of course, this is a pretty simplistic example. In the real world, you'll likely have many more files to embed, and you'll want to organize them in a way that makes sense for your application. Fortunately, Go Embed makes this pretty easy.
One common approach is to create a directory called assets
or static
in your project's root directory, and then place all of your static files in that directory. You can then use Go Embed to embed the entire directory.
How to use Go Embed to load a folder
Now if you want to embed a whole folder, you can also do this too using Go Embed by simply stating the folder relative to the file calling the folder and using an asterisk to denote "all" files, like so:
//go:embed assets/*
var publicAssets embed.FS
func main() {
f, _ := publicAssets.Open("index.html")
var b []byte
_, err := f.Read(b)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(b))
}
// output: <p>hello world</p>
Once you have your embedded file(s) defined and accessibe, you'll now want to either make use of them in your code, or more likely you'll want to expose certain files over http.
How to use Mux with Go Embed FS
In the following Go Embed example, all files contained within the assets
folder will be made public and stored in the Go binary once built. You may wish to have multiple folders, one for public assets and one for internal use only or for private assets. When a request is made to this path, Mux will use the embedded file system to serve the requested file.
//go:embed assets/*
var publicAssets embed.FS
func main() {
fSys, err := fs.Sub(publicAssets, "assets")
if err != nil {
log.Fatal(err)
}
r := mux.NewRouter()
r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.FS(fSys))))
srv := &http.Server{
Handler: r,
Addr: "localhost:8000",
WriteTimeout: 10 * time.Second,
ReadTimeout: 10 * time.Second,
}
srv.ListenAndServe()
}
// assets are made available at http://localhost:8000/assets/
In this example, we're using Mux to set up a handler for serving static files from a directory with the prefix /assets/
. When a request comes in for a file in that directory, the prefix is stripped from the URL path, and the remaining path is used to look up the file in the specified file system. If the file exists, it is served to the client with the appropriate MIME type.
Note that we're using the "http.FS" function to convert the embedded file system into an "http.FileSystem" interface, which is required by the "http.FileServer" function. This allows us to use Mux's built-in file server to serve the embedded files over HTTP.
One advantage of using Go Embed is that it allows you to version your static files along with your code. You can easily roll back to a previous version of your application binary, and know that the static files that were served at that time will be exactly what you expect. This can be especially useful if you have clients that are using an older version of your application and need to access specific files, or you want to roll-back to a stable state.
One thing to keep in mind when using Go Embed however is that it can make your binary file larger. This is because the embedded files are included in the binary itself, which can increase its size. Despite this, this is generally not a concern for smaller applications, and the benefits of using Go Embed often outweigh the potential downside of a slightly larger binary.
One final Go "gotcha" is that you are unable to work up the file system, and instead can only access the current folder or sub-folders. For this reason, you may want to consider where you store your assets
or static
folders within your application.
How can it be used in production?
Work in Golang makes extensive use of this feature by loading in static assets and serving them using a the embedded filesystem for things such as stylesheets, icons, and assets widely used throughout the website. Internally, the system loads templates via Go Embed and injects the content using the mustache templating system. As a result, the production binary is one single file which is easy to build, ship and deploy.