Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serve embedded filesystem from root path of URL

Tags:

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?

like image 480
Sam Herrmann Avatar asked Feb 17 '21 18:02

Sam Herrmann


2 Answers

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))
}
like image 181
Sam Herrmann Avatar answered Nov 07 '22 10:11

Sam Herrmann


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.

like image 40
vearutop Avatar answered Nov 07 '22 12:11

vearutop