Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you use Go 1.16 embed features in subfolders/packages?

Tags:

go

go-http

Go 1.16 is out and I want to use the new embed features. I can get it to work if everything is in the main package. But it's not clear how to handle accessing resources from subfolders/packages. Trying to do it with embed.FS support.

e.g. I have a main.go and also an HTTP handler in a handlers package/folder

If I put the handler in the main, it works. If I put it in the handlers package, it can't find the templates. I get:

handlers/indexHandler.go:11:12: pattern templates: no matching files found exit status 1

Similarly, I can get it to serve an image from the static folder if I serve it from /. But I can't serve both a handler from / and the static/images from /. If I put images on /static/ it can't find the images.

I think it has to do with relative paths. But I can't find the right combination through trial and error... Could it be too early to rely on these features?

Previously I was using go-rice and I did not have these problems. But I would like to use the std library as much as possible.

main.go:

package main

import (...)

//go:embed static
var static embed.FS

func main() {

    fsRoot, _ := fs.Sub(static, "static")
    fsStatic := http.FileServer(http.FS(fsRoot))
    http.Handle("/", fsStatic)
    http.HandleFunc("/index", Index)
    http.ListenAndServe(":8080", nil)
}

handlers/indexHandler.go:

package handlers

import (...)

//go:embed templates
var templates embed.FS

// Index handler
func Index(w http.ResponseWriter, r *http.Request) {

    tmpl := template.New("")
    var err error
    if tmpl, err = tmpl.ParseFS(templates, "simple.gohtml"); err != nil {
        fmt.Println(err)
    }
    if err = tmpl.ExecuteTemplate(w, "simple", nil); err != nil {
        log.Print(err)
    }    
}

Structure is as follows...

.
├── go.mod
├── go.sum
├── handlers
│   └── indexHandler.go
├── main.go
├── static
│   ├── css
│   │   └── layout.css
│   └── images
│       └── logo.png
└── templates
    └── simple.gohtml
like image 548
PrecisionPete Avatar asked Feb 19 '21 22:02

PrecisionPete


1 Answers

I finally figured it out...

You can keep the templates folder in the main folder and embed them from there. Then you need to inject the FS variable into the other handler package. It's always easy after you figure it out.

e.g.

package main

//go:embed templates/*
var templateFs embed.FS

func main() {
    handlers.TemplateFs = templateFs
...
package handlers

var TemplateFs embed.FS

func handlerIndex() {
    ...
    tmpl, err = tmpl.ParseFS(TemplateFs, "templates/layout.gohtml",...
...
like image 184
PrecisionPete Avatar answered Sep 19 '22 16:09

PrecisionPete