I'm working on a small web application in Go that's meant to be used as a tool on a developer's machine to help debug their applications/web services. The interface to the program is a web page which includes not only the HTML, but some JavaScript (for functionality), images and CSS (for styling). I'm planning on open-sourcing this application, so users should simply be able to run a Makefile and all the resources will go where they need to go. However, I'd also like to be able to simply distribute an executable with as few files/dependencies as possible. Is there a good way to bundle the HTML/CSS/JS with the executable, so users only have to download and worry about one file?
Right now, in my app, serving a static file looks a little like this:
// called via http.ListenAndServe
func switchboard(w http.ResponseWriter, r *http.Request) {
// snipped dynamic routing...
// look for static resource
uri := r.URL.RequestURI()
if fp, err := os.Open("static" + uri); err == nil {
defer fp.Close()
staticHandler(w, r, fp)
return
}
// snipped blackhole route
}
So it's pretty simple: if the requested file exists in my static directory, invoke the handler, which simply opens the file and tries to set a good Content-Type
before serving. My thought was that there's no reason this needs to be based on the real filesystem: if there were compiled resources, I could simply index them by the request URI and serve them as such.
If there's not a good way to do this, or I'm barking up the wrong tree by trying to do this, let me know. I just figured the end-user would appreciate as few files as possible to manage.
If there are more appropriate tags than go, please feel free to add them or let me know.
Embedding Text Files
If we're talking about text files, they can easily be embedded in the source code itself. Just use the back quotes to declare the
string
literal like this:Optimization tip:
Since most of the times you will only need to write the resource to an
io.Writer
, you can also store the result of a[]byte
conversion:Only thing you have to be careful about is that raw string literals cannot contain the back quote character (`). Raw string literals cannot contain sequences (unlike the interpreted string literals), so if the text you want to embed does contain back quotes, you have to break the raw string literal and concatenate back quotes as interpreted string literals, like in this example:
Performance is not affected, as these concatenations will be executed by the compiler.
Embedding Binary Files
Storing as a byte slice
For binary files (e.g. images) most compact (regarding the resulting native binary) and most efficient would be to have the content of the file as a
[]byte
in your source code. This can be generated by 3rd party toos/libraries like go-bindata.If you don't want to use a 3rd party library for this, here's a simple code snippet that reads a binary file, and outputs Go source code that declares a variable of type
[]byte
that will be initialized with the exact content of the file:Example output if the file would contain bytes from 0 to 16 (try it on the Go Playground):
Storing as base64
string
If the file is not "too large" (most images/icons qualify), there are other viable options too. You can convert the content of the file to a Base64
string
and store that in your source code. On application startup (func init()
) or when needed, you can decode it to the original[]byte
content. Go has nice support for Base64 encoding in theencoding/base64
package.Converting a (binary) file to base64
string
is as simple as:Store the result base64 string in your source code, e.g. as a
const
.Decoding it is just one function call:
Storing as quoted
string
More efficient than storing as base64, but may be longer in source code is storing the quoted string literal of the binary data. We can obtain the quoted form of any string using the
strconv.Quote()
function:For binary data containing values from 0 up to 64 this is how the output would look like (try it on the Go Playground):
"\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?"
(Note that
strconv.Quote()
appends and prepends a quotation mark to it.)You can directly use this quoted string in your source code, for example:
It is ready to use, no need to decode it; the unquoting is done by the Go compiler, at compile time.
You may also store it as a byte slice should you need it like that:
also there is some exotic way - I use maven plugin to build GoLang projects and it allows to use JCP preprocessor to embed binary blocks and text files into sources. In the case code just look like line below (and some example can be found here)
The go-bindata package looks like it might be what you're interested in.
https://github.com/go-bindata/go-bindata
It will allow you to convert any static file into a function call that can be embedded in your code and will return a byte slice of the file content when called.
As a popular alternative to
go-bindata
mentioned in another answer, mjibson/esc also embeds arbitrary files, but handles directory trees particularly conveniently.