func Static(opts ...StaticOptions) Handler {
	var opt StaticOptions
	if len(opts) > 0 {
		opt = opts[0]
	}
	parseStaticOptions := func(opts StaticOptions) StaticOptions {
		if opts.Directory == "" {
			opts.Directory = "public"
		}
		if opts.FileSystem == nil {
			opts.FileSystem = http.Dir(opts.Directory)
		}
		if opts.Prefix != "" {
			opts.Prefix = "/" + strings.Trim(opts.Prefix, "/")
		}
		if opts.Index == "" {
			opts.Index = "index.html"
		}
		return opts
	}
	opt = parseStaticOptions(opt)
	return LoggerInvoker(func(c Context, log *log.Logger) {
		if c.Request().Method != http.MethodGet && c.Request().Method != http.MethodHead {
			return
		}
		file := c.Request().URL.Path
		if opt.Prefix != "" {
			if !strings.HasPrefix(file, opt.Prefix) {
				return
			}
			file = file[len(opt.Prefix):]
			if file != "" && file[0] != '/' {
				return
			}
		}
		f, err := opt.FileSystem.Open(file)
		if err != nil {
			return
		}
		defer func() { _ = f.Close() }()
		fi, err := f.Stat()
		if err != nil {
			return  
		}
		if fi.IsDir() {
			redirPath := path.Clean(c.Request().URL.Path)
			if strings.HasSuffix(c.Request().URL.Path, "/") && !strings.HasSuffix(redirPath, "/") {
				redirPath += "/"
			}
			if !strings.HasSuffix(redirPath, "/") {
				http.Redirect(c.ResponseWriter(), c.Request().Request, redirPath+"/", http.StatusFound)
				return
			}
			file = path.Join(file, opt.Index)
			index, err := opt.FileSystem.Open(file)
			if err != nil {
				return
			}
			defer func() { _ = index.Close() }()
			fi, err = index.Stat()
			if err != nil || fi.IsDir() {
				return
			}
			_ = f.Close()
			f = index
		}
		if opt.EnableLogging {
			log.Println("[Static] Serving " + file)
		}
		if opt.Expires != nil {
			c.ResponseWriter().Header().Set("Expires", opt.Expires())
		}
		if opt.SetETag {
			etag := generateETag(fi.Size(), fi.Name(), fi.ModTime())
			c.ResponseWriter().Header().Set("ETag", etag)
			if c.Request().Header.Get("If-None-Match") == etag {
				c.ResponseWriter().WriteHeader(http.StatusNotModified)
				return
			}
		}
		http.ServeContent(c.ResponseWriter(), c.Request().Request, file, fi.ModTime(), f)
	})
}
Static returns a middleware handler that serves static files in the given
directory.
package flamego
import (
	"encoding/base64"
	"fmt"
	"log"
	"net/http"
	"path"
	"strings"
	"time"
)
type StaticOptions struct {
	Directory string
	FileSystem http.FileSystem
	Prefix string
	Index string
	Expires func() string
	SetETag bool
	EnableLogging bool
}
func generateETag(size int64, name string, modtime time.Time) string {
	value := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%d%s%s", size, name, modtime.UTC().Format(http.TimeFormat))))
	return `"` + value + `"`
}