Skip to content

Instantly share code, notes, and snippets.

@philcleveland
Last active November 26, 2022 20:26
Show Gist options
  • Save philcleveland/caa18e00970e9d42d957 to your computer and use it in GitHub Desktop.
Save philcleveland/caa18e00970e9d42d957 to your computer and use it in GitHub Desktop.
GoLang template with image
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
Index page.
<!-- This image always fails to load when served from go, but is fine if I just double click the html file -->
<img src="someImg.png" alt="Cannot load image" style="width: 200px;height: 400px">
</body>
</html>
package main
import (
"html/template"
"net/http"
)
var (
templates *template.Template
)
func main() {
// This is the only way I have found to be able to serve files requested in the templates
http.Handle("/static/img/", http.StripPrefix("/static/img/",
http.FileServer(http.Dir(path.Join(rootdir, "/static/img/")))))
http.Handle("/static/css/", http.StripPrefix("/static/css/",
http.FileServer(http.Dir(path.Join(rootdir, "/static/css/")))))
http.HandleFunc("/", index)
templates = template.Must(template.ParseFiles("index.html"))
http.ListenAndServe(":8080", nil)
}
func index(w http.ResponseWriter, r *http.Request) {
err := templates.ExecuteTemplate(w, "index.html", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
@kylehqcom
Copy link

You can also directly serve a single static file with http.ServeFile(w, r, "/static/img/someImg.png")

For numerous static files, then yes, use a http.FileServer - I normally have a separate function that I call with different "root" http.FileSystem types. Having a single method makes life much easier to handle all the http.StripPrefix steps etc.

Furthermore, you can also alter the path to your static assets via Template Funcs - eg in the head of my html I may have

<link rel="preload" as="style" media="all" href="{{appCSS}}" />
<link rel="stylesheet" media="all" href="{{appCSS}}">

I then add to the template.Funcs the template.FuncMap which will return a cached version if it's already been set and will suffix the path with a unix timestamp so that any changes will cache bust eg:

template.FuncMap{
    "appCSS": func() string {
        // Check if we already have a asset path to return, only if caching enabled
        asset := "mysite.css"
        if !cacheAssetsDisabled {
            if cached, ok := assetCachePaths["css_"+asset]; ok {
                return cached
            }
        }

        workDir, _ := os.Getwd()
        cssPath := "src/public/css/"
        cssSrcPath := "/public/css/"
        cssFilePath := filepath.Join(workDir, cssPath, asset)
        stat, err := os.Stat(cssFilePath)
        if err != nil {
            logger.Error().Err(err).Caller().Str("file", cssFilePath).Msg("Unable to locate css file")
            return ""
        }

        cssSrcPath = cssSrcPath + asset
        cachedPath := fmt.Sprintf("%s?%d", cssSrcPath, stat.ModTime().Unix())
        assetCachePaths["css_"+asset] = cachedPath
        return cachedPath
    },
    ...  

I do the same for my JS files also. Coming from other languages, I found that Go templates seemed to be pretty cumbersome. I wrote a few pieces here if it helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment