Skip to content

Commit b830c4e

Browse files
committed
Improve filesystem support.
1 parent af2a49d commit b830c4e

File tree

6 files changed

+68
-32
lines changed

6 files changed

+68
-32
lines changed

context_fs_go1.16.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ func (c *context) File(file string) error {
1515
return fsFile(c, file, c.echo.Filesystem)
1616
}
1717

18+
// FileFS serves file from given file system.
19+
//
20+
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
21+
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
22+
// including `assets/images` as their prefix.
1823
func (c *context) FileFS(file string, filesystem fs.FS) error {
1924
return fsFile(c, file, filesystem)
2025
}
@@ -28,7 +33,7 @@ func fsFile(c Context, file string, filesystem fs.FS) error {
2833

2934
fi, _ := f.Stat()
3035
if fi.IsDir() {
31-
file = filepath.ToSlash(filepath.Join(file, indexPage))
36+
file = filepath.Join(file, indexPage)
3237
f, err = filesystem.Open(file)
3338
if err != nil {
3439
return ErrNotFound

context_fs_go1.16_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
package echo
55

66
import (
7-
testify "github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/assert"
88
"io/fs"
99
"net/http"
1010
"net/http/httptest"
@@ -62,18 +62,18 @@ func TestContext_File(t *testing.T) {
6262

6363
err := handler(c)
6464

65-
testify.Equal(t, tc.expectStatus, rec.Code)
65+
assert.Equal(t, tc.expectStatus, rec.Code)
6666
if tc.expectError != "" {
67-
testify.EqualError(t, err, tc.expectError)
67+
assert.EqualError(t, err, tc.expectError)
6868
} else {
69-
testify.NoError(t, err)
69+
assert.NoError(t, err)
7070
}
7171

7272
body := rec.Body.Bytes()
7373
if len(body) > len(tc.expectStartsWith) {
7474
body = body[:len(tc.expectStartsWith)]
7575
}
76-
testify.Equal(t, tc.expectStartsWith, body)
76+
assert.Equal(t, tc.expectStartsWith, body)
7777
})
7878
}
7979
}
@@ -118,18 +118,18 @@ func TestContext_FileFS(t *testing.T) {
118118

119119
err := handler(c)
120120

121-
testify.Equal(t, tc.expectStatus, rec.Code)
121+
assert.Equal(t, tc.expectStatus, rec.Code)
122122
if tc.expectError != "" {
123-
testify.EqualError(t, err, tc.expectError)
123+
assert.EqualError(t, err, tc.expectError)
124124
} else {
125-
testify.NoError(t, err)
125+
assert.NoError(t, err)
126126
}
127127

128128
body := rec.Body.Bytes()
129129
if len(body) > len(tc.expectStartsWith) {
130130
body = body[:len(tc.expectStartsWith)]
131131
}
132-
testify.Equal(t, tc.expectStartsWith, body)
132+
assert.Equal(t, tc.expectStartsWith, body)
133133
})
134134
}
135135
}

echo_fs_go1.16.go

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ import (
1616
type filesystem struct {
1717
// Filesystem is file system used by Static and File handlers to access files.
1818
// Defaults to os.DirFS(".")
19+
//
20+
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
21+
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
22+
// including `assets/images` as their prefix.
1923
Filesystem fs.FS
2024
}
2125

@@ -26,12 +30,8 @@ func createFilesystem() filesystem {
2630
}
2731

2832
// Static registers a new route with path prefix to serve static files from the provided root directory.
29-
func (e *Echo) Static(pathPrefix, root string) *Route {
30-
subFs, err := subFS(e.Filesystem, root)
31-
if err != nil {
32-
// happens when `root` contains invalid path according to `fs.ValidPath` rules and we are unable to create FS
33-
panic(fmt.Errorf("invalid root given to echo.Static, err %w", err))
34-
}
33+
func (e *Echo) Static(pathPrefix, fsRoot string) *Route {
34+
subFs := MustSubFS(e.Filesystem, fsRoot)
3535
return e.Add(
3636
http.MethodGet,
3737
pathPrefix+"*",
@@ -40,11 +40,15 @@ func (e *Echo) Static(pathPrefix, root string) *Route {
4040
}
4141

4242
// StaticFS registers a new route with path prefix to serve static files from the provided file system.
43-
func (e *Echo) StaticFS(pathPrefix string, fileSystem fs.FS) *Route {
43+
//
44+
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
45+
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
46+
// including `assets/images` as their prefix.
47+
func (e *Echo) StaticFS(pathPrefix string, filesystem fs.FS) *Route {
4448
return e.Add(
4549
http.MethodGet,
4650
pathPrefix+"*",
47-
StaticDirectoryHandler(fileSystem, false),
51+
StaticDirectoryHandler(filesystem, false),
4852
)
4953
}
5054

@@ -125,3 +129,17 @@ func subFS(currentFs fs.FS, root string) (fs.FS, error) {
125129
}
126130
return fs.Sub(currentFs, root)
127131
}
132+
133+
// MustSubFS creates sub FS from current filesystem or panic on failure.
134+
// Panic happens when `fsRoot` contains invalid path according to `fs.ValidPath` rules.
135+
//
136+
// MustSubFS is helpful when dealing with `embed.FS` because for example `//go:embed assets/images` embeds files with
137+
// paths including `assets/images` as their prefix. In that case use `fs := echo.MustSubFS(fs, "rootDirectory") to
138+
// create sub fs which uses necessary prefix for directory path.
139+
func MustSubFS(currentFs fs.FS, fsRoot string) fs.FS {
140+
subFs, err := subFS(currentFs, fsRoot)
141+
if err != nil {
142+
panic(fmt.Errorf("can not create sub FS, invalid root given, err: %w", err))
143+
}
144+
return subFs
145+
}

echo_fs_go1.16_test.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func TestEcho_StaticFS(t *testing.T) {
1818
name string
1919
givenPrefix string
2020
givenFs fs.FS
21+
givenFsRoot string
2122
whenURL string
2223
expectStatus int
2324
expectHeaderLocation string
@@ -31,6 +32,14 @@ func TestEcho_StaticFS(t *testing.T) {
3132
expectStatus: http.StatusOK,
3233
expectBodyStartsWith: string([]byte{0x89, 0x50, 0x4e, 0x47}),
3334
},
35+
{
36+
name: "ok, from sub fs",
37+
givenPrefix: "/images",
38+
givenFs: MustSubFS(os.DirFS("./_fixture/"), "images"),
39+
whenURL: "/images/walle.png",
40+
expectStatus: http.StatusOK,
41+
expectBodyStartsWith: string([]byte{0x89, 0x50, 0x4e, 0x47}),
42+
},
3443
{
3544
name: "No file",
3645
givenPrefix: "/images",
@@ -135,7 +144,12 @@ func TestEcho_StaticFS(t *testing.T) {
135144
for _, tc := range testCases {
136145
t.Run(tc.name, func(t *testing.T) {
137146
e := New()
138-
e.StaticFS(tc.givenPrefix, tc.givenFs)
147+
148+
tmpFs := tc.givenFs
149+
if tc.givenFsRoot != "" {
150+
tmpFs = MustSubFS(tmpFs, tc.givenFsRoot)
151+
}
152+
e.StaticFS(tc.givenPrefix, tmpFs)
139153

140154
req := httptest.NewRequest(http.MethodGet, tc.whenURL, nil)
141155
rec := httptest.NewRecorder()
@@ -229,12 +243,12 @@ func TestEcho_StaticPanic(t *testing.T) {
229243
{
230244
name: "panics for ../",
231245
givenRoot: "../assets",
232-
expectError: "invalid root given to echo.Static, err sub ../assets: invalid name",
246+
expectError: "can not create sub FS, invalid root given, err: sub ../assets: invalid name",
233247
},
234248
{
235249
name: "panics for /",
236250
givenRoot: "/assets",
237-
expectError: "invalid root given to echo.Static, err sub /assets: invalid name",
251+
expectError: "can not create sub FS, invalid root given, err: sub /assets: invalid name",
238252
},
239253
}
240254

group_fs_go1.16.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,26 @@
44
package echo
55

66
import (
7-
"fmt"
87
"io/fs"
98
"net/http"
109
)
1110

1211
// Static implements `Echo#Static()` for sub-routes within the Group.
13-
func (g *Group) Static(pathPrefix, root string) {
14-
subFs, err := subFS(g.echo.Filesystem, root)
15-
if err != nil {
16-
// happens when `root` contains invalid path according to `fs.ValidPath` rules and we are unable to create FS
17-
panic(fmt.Errorf("invalid root given to group.Static, err %w", err))
18-
}
12+
func (g *Group) Static(pathPrefix, fsRoot string) {
13+
subFs := MustSubFS(g.echo.Filesystem, fsRoot)
1914
g.StaticFS(pathPrefix, subFs)
2015
}
2116

2217
// StaticFS implements `Echo#StaticFS()` for sub-routes within the Group.
23-
func (g *Group) StaticFS(pathPrefix string, fileSystem fs.FS) {
18+
//
19+
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
20+
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
21+
// including `assets/images` as their prefix.
22+
func (g *Group) StaticFS(pathPrefix string, filesystem fs.FS) {
2423
g.Add(
2524
http.MethodGet,
2625
pathPrefix+"*",
27-
StaticDirectoryHandler(fileSystem, false),
26+
StaticDirectoryHandler(filesystem, false),
2827
)
2928
}
3029

group_fs_go1.16_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ func TestGroup_StaticPanic(t *testing.T) {
8282
{
8383
name: "panics for ../",
8484
givenRoot: "../images",
85-
expectError: "invalid root given to group.Static, err sub ../images: invalid name",
85+
expectError: "can not create sub FS, invalid root given, err: sub ../images: invalid name",
8686
},
8787
{
8888
name: "panics for /",
8989
givenRoot: "/images",
90-
expectError: "invalid root given to group.Static, err sub /images: invalid name",
90+
expectError: "can not create sub FS, invalid root given, err: sub /images: invalid name",
9191
},
9292
}
9393

0 commit comments

Comments
 (0)