Go is becoming increasingly popular with building web applications. It’s fast, lightweight and easy to learn. Another great feature is to compile your applications into a single redistributable binary. A lot of web applications depend on external assets, however. For the application to work you still need to package your css, JavaScript and image files along with the executable. Let’s see if we can fix this.
Imagine you have a single page web application. Your application code is saved in main.go
. Your frontend code is saved in a folder called assets
and called app.js
. You also have a style.css
and a few images. Normally you need to make sure that assets folder exists along side your main
executable wherever you have it deployed. If not your application will complain with a bunch of 404 not found
errors.
In Go, it’s possible to convert all of those files to pure Go code and then serve them up like they were sitting on the filesystem. We can accomplish this with two packages jteeuwen/go-bindata
and elazarl/go-bindata-assetfs
.
The first thing we need to do is install the packages.
$ go get github.com/jteeuwen/go-bindata/...
$ go get github.com/elazarl/go-bindata-assetfs/...
Now we can convert our assets to Go code using the go-bindata
CLI tool.
$ go-bindata assets/
This will create a file called bindata.go
in your current directory. All the code in that file will be scoped to the main
package. To access any of our files we can use the Asset(string) ([]byte, error)
function. Calling Asset("assets/app.js")
will return a []byte
slice of the app.js
file which we can echo through our web application manually if we wish. An easier method would be to create an endpoint that serves all of those assets as if they were on a fileserver. This is where go-bindata-assetfs
comes in.
Here is an example.
package main
import (
"log"
"net/http"
"github.com/elazarl/go-bindata-assetfs"
)
func main() {
// Use binary asset FileServer
http.Handle("/",
http.FileServer(
&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo, Prefix: "assets"}))
log.Println("http server started on :8000")
err := http.ListenAndServe(":8000", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
The go-bindata-assetfs
package includes a Struct
type called AssetFS
that implements the http.Filesystem
interface which looks like this.
type FileSystem interface {
Open(name string) (File, error)
}
To initialize a new AssetFS
we need to pass a few of the helper functions generated by go-bindata
. The final property is Prefix
which sets the root of the FileServer
as whatever we specify.
In this example we are now serving files at /
and any files that were in assets/
are now accessible from that path. It would be simple to place an index.html
file to use as an entrypoint for a single page application.
Now we no longer have to distribute our assets along with the final executable. This makes our deployments just a little bit easier and more managable.