diff options
Diffstat (limited to 'echo/static_file.go')
-rw-r--r-- | echo/static_file.go | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/echo/static_file.go b/echo/static_file.go new file mode 100644 index 0000000..9723db7 --- /dev/null +++ b/echo/static_file.go | |||
@@ -0,0 +1,71 @@ | |||
1 | package echo | ||
2 | |||
3 | import ( | ||
4 | "io" | ||
5 | "io/fs" | ||
6 | "net/http" | ||
7 | "net/url" | ||
8 | "path/filepath" | ||
9 | |||
10 | "github.com/labstack/echo/v4" | ||
11 | ) | ||
12 | |||
13 | type routeFunc func(string, echo.HandlerFunc, ...echo.MiddlewareFunc) *echo.Route | ||
14 | |||
15 | func StaticFS(get routeFunc, f fs.FS, prefix, root string, m ...echo.MiddlewareFunc) *echo.Route { | ||
16 | if root == "" { | ||
17 | root = "." // For security we want to restrict to CWD. | ||
18 | } | ||
19 | |||
20 | h := func(c echo.Context) error { | ||
21 | p, err := url.PathUnescape(c.Param("*")) | ||
22 | if err != nil { | ||
23 | return err | ||
24 | } | ||
25 | |||
26 | name := filepath.Join(root, filepath.Clean("/"+p)) // "/"+ for security | ||
27 | fp, err := f.Open(name) | ||
28 | if err != nil { | ||
29 | // The access path does not exist | ||
30 | return echo.NotFoundHandler(c) | ||
31 | } | ||
32 | defer fp.Close() | ||
33 | |||
34 | fi, err := fp.Stat() | ||
35 | if err != nil { | ||
36 | // The access path does not exist | ||
37 | return echo.NotFoundHandler(c) | ||
38 | } | ||
39 | |||
40 | // If the request is for a directory and does not end with "/" | ||
41 | p = c.Request().URL.Path // path must not be empty. | ||
42 | if fi.IsDir() && p[len(p)-1] != '/' { | ||
43 | // Redirect to ends with "/" | ||
44 | // return c.Redirect(http.StatusMovedPermanently, p+"/") | ||
45 | // TODO: Serve an index.html if there is one for this dir | ||
46 | return echo.NotFoundHandler(c) | ||
47 | } | ||
48 | |||
49 | fs, ok := fp.(io.ReadSeeker) | ||
50 | if !ok { | ||
51 | c.Logger().Errorf("File %s is not a io.ReadSeeker", p) | ||
52 | return echo.ErrInternalServerError | ||
53 | } | ||
54 | |||
55 | http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), fs) | ||
56 | return nil | ||
57 | } | ||
58 | |||
59 | // Handle added routes based on trailing slash: | ||
60 | // /prefix => exact route "/prefix" + any route "/prefix/*" | ||
61 | // /prefix/ => only any route "/prefix/*" | ||
62 | if prefix != "" { | ||
63 | if prefix[len(prefix)-1] == '/' { | ||
64 | // Only add any route for intentional trailing slash | ||
65 | return get(prefix+"*", h, m...) | ||
66 | } | ||
67 | get(prefix, h, m...) | ||
68 | } | ||
69 | |||
70 | return get(prefix+"/*", h, m...) | ||
71 | } | ||