What is the best way to link static resources in a Go program?

I am working on a small Go web application that is intended to be used as a tool on a developer's machine to help debug my applications / web services. The program interface is a web page that includes not only HTML, but also some JavaScript (for functionality), images, and CSS (for styling). I plan to open this application so that users can simply run the Makefile and all resources will go where they need to go. However, I would also like to be able to simply distribute the executable with as many files / dependencies as possible. Is there a good way to associate HTML / CSS / JS with an executable, so users have to download and worry about one file?




Right now, in my application, serving a static file looks something 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, call a handler that simply opens the file and tries to set a good Content-Type before serving. I thought there was no reason why this should be based on a real file system: if there were compiled resources, I could just index them by request URI and serve them as such.

If there is no good way to do this, or I bark the wrong tree, trying to do this, let me know. I just decided that the end user would rate as few files as possible.

If there are more suitable tags than go , feel free to add them or let me know.

+86
go
Dec 16 '12 at 18:52
source share
5 answers

The go-bindata package looks like it interests you.

https://github.com/go-bindata/go-bindata

This will allow you to convert any static file into a function call that can be embedded in your code, and when called, will return a piece of the byte of the contents of the file.

+72
Dec 16 '12 at 19:01
source share

Embed Text Files

If we are talking about text files, they can be easily embedded in the source code itself. Just use backticks to declare a string literal as follows:

 const html = ' <html> <body>Example embedded HTML content.</body> </html> ' // Sending it: w.Write([]byte(html)) // w is an io.Writer 

Optimization tip:

Since in most cases you only need to write the resource to io.Writer , you can also save the result of the conversion []byte :

 var html = []byte(' <html><body>Example...</body></html> ') // Sending it: w.Write(html) // w is an io.Writer 

The only thing you should be careful with is that raw string literals cannot contain the backtick character ('). Raw string literals cannot contain sequences (as opposed to interpreted string literals), so if the text you want to embed contains backquotes, you must break the raw string literal and concatenate backquotes as interpreted string literals, as in this example:

 var html = '<p>This is a back quote followed by a dot: ' + "'" + '.</p>' 

This does not affect performance, since these associations will be performed by the compiler.

Embedding Binary Files

Byte fragment storage

For binary files (for example, images) the most compact (relative to the resulting own binary file) and the most efficient will be the contents of the file in the form of []byte in the source code. This can be generated by third-party tasks / libraries such as go-bindata .

If you do not want to use a third-party library for this, here is a simple snippet of code that reads a binary file and displays the Go source code, which declares a variable []byte type []byte that will be initialized with the exact contents of the file:

 imgdata, err := ioutil.ReadFile("someimage.png") if err != nil { panic(err) } fmt.Print("var imgdata = []byte{") for i, v := range imgdata { if i > 0 { fmt.Print(", ") } fmt.Print(v) } fmt.Println("}") 

Example output if the file contains bytes from 0 to 16 (try on the Go Playground ):

 var imgdata = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} 

Saving as string base64

If the file is not “too large” (most of the images / icons meet the requirements), there are other viable options. You can convert the contents of the file to a string Base64 and save it in the source code. When you run the application ( func init() ) or if necessary, you can decode it into the original []byte content. Go has good support for Base64 encoding in the encoding/base64 package.

Converting a (binary) file to a base64 string is as simple as:

 data, err := ioutil.ReadFile("someimage.png") if err != nil { panic(err) } fmt.Println(base64.StdEncoding.EncodeToString(data)) 

Save the base64 result string in your source code, for example, as const .

Decryption is just one function call:

 const imgBase64 = "<insert base64 string here>" data, err := base64.StdEncoding.DecodeString(imgBase64) // data is of type []byte 

Query string storage

More efficient than storing as base64, but it may be longer in the source code to store a string literal in binary quotation marks. We can get any string in quotes using strconv.Quote() :

 data, err := ioutil.ReadFile("someimage.png") if err != nil { panic(err) } fmt.Println(strconv.Quote(string(data)) 

For binary data containing values ​​from 0 to 64, here is what the output will look like (try 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 appends a quotation mark to it.)

You can directly use this string in quotation marks in your source code, for example:

 const imgdata = "\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:;<=>?" 

It is ready to use; there is no need to decode it; citation cancellation is done by the Go compiler at compile time.

You can also save it as a piece of byte if you need it:

 var imgdata = []byte("\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:;<=>?") 
+30
Jan 21 '15 at 15:51
source share

there is also some exotic way - I use the maven plugin to create GoLang projects and allows me to use the JCP preprocessor to embed binary blocks and text files in sources. The code below simply looks like the line below ( and an example can be found here )

 var imageArray = []uint8{/*$binfile("./image.png","uint8[]")$*/} 
+2
Feb 14 '17 at 6:23
source share

As a popular alternative to go-bindata mentioned in another answer, mjibson / esc also embeds arbitrary files, but it handles directory trees especially conveniently.

+1
Aug 24 '18 at 8:21
source share
0
May 6 '19 at 2:20
source share



All Articles