Serve embedded filesystem from root path of URL - go

Go 1.16 added the new embed package. I would like to use this package to embed a directory and serve it over HTTP. Consider the following setup:
myproject/
|-- main.go
|-- static/
| |-- index.html
| |-- styles.css
| |-- scripts.js
package main
import (
"embed"
"log"
"net/http"
)
//go:embed static
var staticFS embed.FS
func main() {
http.Handle("/", http.FileServer(http.FS(staticFS)))
log.Fatal(http.ListenAndServe(":8080", nil))
}
With this setup, my expectation is that I can point my browser to localhost:8080 and have it load index.html. What I am observing instead, is that I need to point my browser to localhost:8080/static to have it load index.html.
How can an embedded filesystem be served from the root path of the URL?

When declaring a variable of type embed.FS, that variable represents a filesystem that already contains a root directory. All resources from the //go:embed directive are copied into this root directory of the filesystem. That means that the staticFS variable does not refer to the static folder that was being embedded directly, but to the root directory that contains the static folder. With that in mind, to achieve the desired result of being able to access the static folder from localhost:8080, Go's fs.Sub can be used to pass a filesystem to the server where the static folder is the root:
package main
import (
"embed"
"io/fs"
"log"
"net/http"
)
//go:embed static
var embeddedFS embed.FS
func main() {
serverRoot, err := fs.Sub(embeddedFS, "static")
if err != nil {
log.Fatal(err)
}
http.Handle("/", http.FileServer(http.FS(serverRoot)))
log.Fatal(http.ListenAndServe(":8080", nil))
}

Alternatively to fs.Sub you can declare embedding inside the static directory.
package static
//go:embed *.html *.css *.js
var FS embed.FS
And then import it.
package main
import (
"embed"
"log"
"net/http"
"import/path/to/static"
)
func main() {
http.Handle("/", http.FileServer(http.FS(static.FS)))
log.Fatal(http.ListenAndServe(":8080", nil))
}
The downside of adding embedding into static directory is that *.go file may be also added as embedded unless strict file masks are used (e.g. //go:embed *.html *js instead of //go:embed *).
Edit: Removed additionally proposed http.StripPrefix because it does not actually help with issue.

Related

Golang template can't access file in embedFS

my golang code arch is as bellow:
├── embeded.go
├── go.mod
├── json
│   └── file
└── main.go
and this is my embede.go code:
package main
import "embed"
//go:embed json/*
var templatesFS embed.FS
func TemplatesFS() embed.FS {
return templatesFS
}
now in my main.go I can't access file in json directory:
package main
import (
"fmt"
"log"
"os"
"text/template"
)
func main() {
tmpl := template.Must(
template.New("json/file").
ParseFS(TemplatesFS(), "json/file"))
if err := tmpl.Execute(os.Stdout, "config"); err != nil {
log.Fatal(err)
}
}
when I run above code I got error template: json/file: "json/file" is an incomplete or empty template
but I can access the file like this:
file, err := TemplatesFS().ReadFile("json/file")
so why I can't access it in templte.execute ?
How Can I Solve It ?
The template parser successfully read a template from the embedded file system.
The Execute method reports that the template tmpl is incomplete. The variable tmpl is set to the template created by the call to New. The template is incomplete because the application did not parse a template with the template's name, json/file.
ParseFS names templates using the file's base name. Fix by using using the file's base name in the call to New.
tmpl := template.Must(
template.New("file").ParseFS(TemplatesFS(), "json/file"))

go:embed fs only works for the root with http server?

I have the following directory structure:
web/
dist/
index.html
main.go
the content of main.go:
package main
import (
"embed"
"io/fs"
"net/http"
)
//go:embed web/dist
var webfs embed.FS
func main() {
mux := http.NewServeMux()
sub, err := fs.Sub(webfs, "web/dist")
if err != nil {
panic(err)
}
mux.Handle("/", http.FileServer(http.FS(sub)))
http.ListenAndServe(":2222", mux)
}
When the code is run, I could get the content of index.html through http://127.0.0.1:2222. However, when I changed the line to:
mux.Handle("/admin/", http.FileServer(http.FS(sub)))
I get 404 when accessing http://127.0.0.1:2222/admin/.
You will need to strip the /admin/ off the request path (otherwise the file server will be looking in web/dist/admin). e.g.
mux.Handle("/admin/", http.StripPrefix("/admin/", http.FileServer(http.FS(sub))))
See the docs for StripPrefix for more info.

Multiple static files directories

In go, we can handle static files, by defining their directory as static directory as shown below:
fs := http.FileServer(http.Dir("./static/"))
http.Handle("/files/", fs)
Where static files folder static should be beside the binary.
Another way, is to use the new //go embed as:
//go:embed static
var staticFiles embed.FS
// http.FS can be used to create a http Filesystem
var staticFS = http.FS(staticFiles)
fs := http.FileServer(staticFS) // embeded static files
// Serve static files
http.Handle("/static/", fs)
But what if I want to have most of my static files embedded in the binary, and some are not, that can be used alongside the binary, I tried to mix both method defined above, but did not work, only the embedded ones run smoothly, below code failed, any thought?:
//go:embed static
var staticFiles embed.FS
// http.FS can be used to create a http Filesystem
var staticFS = http.FS(staticFiles)
fs := http.FileServer(staticFS) // embeded static files
// Serve static files
http.Handle("/static/", fs)
www := http.FileServer(http.Dir("./files/")) // side static files, to be beside binary
// Serve static files
http.Handle("/files/", www)
I found it, was missing http.StripPrefix, below worked perfectly with me, and have multiple static folders:
package main
import (
"embed"
"encoding/json"
"fmt"
"log"
"net/http"
)
//go:embed static
var staticFiles embed.FS
func main() {
go func() {
http.HandleFunc("/favicon.ico", func(rw http.ResponseWriter, r *http.Request) {})
// http.FS can be used to create a http Filesystem
var staticFS = http.FS(staticFiles)
fs := http.FileServer(staticFS) // embeded static files
// Serve static files, to be embedded in the binary
http.Handle("/static/", fs)
// Serve public files, to be beside binary
http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("./files"))))
http.HandleFunc("/getSkills", getSkills)
log.Println("Listening on :3000...")
err := http.ListenAndServe(":3000", nil)
if err != nil {
log.Fatal(err)
}
}

go 1.16 embed - strip directory name

i was previously using statik to embed files into a Go application.
with Go 1.16 i can remove that deps
for example:
//go:embed static
var static embed.FS
fs := http.FileServer(http.FS(static))
http.Handle("/", fs)
this will serve the ./static/ directory from http://.../static/
Is there a way I can serve that directory from / root path, without /static?
Use fs.Sub:
Sub returns an FS corresponding to the subtree rooted at fsys's dir.
package main
import (
"embed"
"io/fs"
"log"
"net/http"
)
//go:embed static
var static embed.FS
func main() {
subFS, _ := fs.Sub(static, "static")
http.Handle("/", http.FileServer(http.FS(subFS)))
log.Fatal(http.ListenAndServe(":4000", nil))
}
fs.Sub is also useful in combination with http.StripPrefix to "rename" a directory. For instance, to "rename" the directory static to public, such that a request for /public/index.html serves static/index.html:
//go:embed static
var static embed.FS
subFS, _ := fs.Sub(static, "static")
http.Handle("/", http.StripPrefix("/public", http.FileServer(http.FS(subFS))))
Alternatively, create a .go file in the static directory and move the embed directive there (//go:embed *). That matches a little more closely what the statik tool does (it creates a whole new package), but is usually unnecessary thanks to fs.Sub.
// main.go
package main
import (
"my.module/static"
"log"
"net/http"
)
func main() {
http.Handle("/", http.FileServer(http.FS(static.FS)))
log.Fatal(http.ListenAndServe(":4000", nil))
}
// static/static.go
package static
import "embed"
//go:embed *
var FS embed.FS

Serve static files with golang

I'm trying to develop a simple web application but I'mhaving problem with serving my static files.
The file structure is:
main
--main.go
-serve
--listenAndServe.go
--templates
---login.html
---assets
----css
----fonts
----js
my code is this:
import (
"log"
"net/http"
"time"
"github.com/gorilla/mux"
)
var (
router = mux.NewRouter()
)
func (c *Conn) ListenAndServe() {
fs := http.FileServer(http.Dir("./templates/assets"))
http.Handle("/assets/", http.StripPrefix("/assets/", fs))
router.HandleFunc("/", c.IndexPageHandler)
router.HandleFunc("/login.html", c.LoginPageHandler)
log.Println("Listening...")
http.Handle("/", router)
muxWithMiddlewares := http.TimeoutHandler(router, time.Minute*30,
"Timeout!")
http.ListenAndServe(":8080", muxWithMiddlewares)
}
But for some reason when I run it from main.go it serves the html but not the assets. I would really apreciate some tips. Thanks!
Try This:
mux.Handle("/static/", http.StripPrefix("/static", fileServer))
Note that static, in your case assets only has a single forward slash within the stripPreFix function.
Hope this helps.

Resources