diff --git a/README.md b/README.md index e5fc6aa..3fa26d2 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ import "github.com/gin-contrib/static" See the [example](_example) +#### Serve local file + ```go package main @@ -48,6 +50,39 @@ func main() { c.String(200, "test") }) // Listen and Server in 0.0.0.0:8080 + r.Run(":8080") +} +``` + +#### Serve embed folder + +```go +package main + +import ( + "embed" + "fmt" + "net/http" + + "github.com/gin-contrib/static" + "github.com/gin-gonic/gin" +) + +//go:embed data +var server embed.FS + +func main() { + r := gin.Default() + r.Use(static.Serve("/", static.EmbedFolder(server, "data/server"))) + r.GET("/ping", func(c *gin.Context) { + c.String(200, "test") + }) + r.NoRoute(func(c *gin.Context) { + fmt.Printf("%s doesn't exists, redirect on /\n", c.Request.URL.Path) + c.Redirect(http.StatusMovedPermanently, "/") + }) + // Listen and Server in 0.0.0.0:8080 + r.Run(":8080") if err := r.Run(":8080"); err != nil { log.Fatal(err) } diff --git a/embed_folder.go b/embed_folder.go new file mode 100644 index 0000000..15c211c --- /dev/null +++ b/embed_folder.go @@ -0,0 +1,27 @@ +package static + +import ( + "embed" + "io/fs" + "log" + "net/http" +) + +type embedFileSystem struct { + http.FileSystem +} + +func (e embedFileSystem) Exists(prefix string, path string) bool { + _, err := e.Open(path) + return err == nil +} + +func EmbedFolder(fsEmbed embed.FS, targetPath string) ServeFileSystem { + fsys, err := fs.Sub(fsEmbed, targetPath) + if err != nil { + log.Fatalf("static.EmbedFolder - Invalid targetPath value - %s", err) + } + return embedFileSystem{ + FileSystem: http.FS(fsys), + } +} diff --git a/embed_folder_test.go b/embed_folder_test.go new file mode 100644 index 0000000..65ea122 --- /dev/null +++ b/embed_folder_test.go @@ -0,0 +1,40 @@ +package static + +import ( + "embed" + "fmt" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +//go:embed test/data/server +var server embed.FS + +var embedTests = []struct { + targetURL string // input + httpCode int // expected http code + httpBody string // expected http body + name string // test name +}{ + {"/404.html", 301, "Moved Permanently.\n\n", "Unknown file"}, + {"/", 200, "

Hello Embed

", "Root"}, + {"/index.html", 301, "", "Root by file name automatic redirect"}, + {"/static.html", 200, "

Hello Gin Static

", "Other file"}, +} + +func TestEmbedFolder(t *testing.T) { + router := gin.New() + router.Use(Serve("/", EmbedFolder(server, "test/data/server"))) + router.NoRoute(func(c *gin.Context) { + fmt.Printf("%s doesn't exists, redirect on /\n", c.Request.URL.Path) + c.Redirect(301, "/") + }) + + for _, tt := range embedTests { + w := PerformRequest(router, "GET", tt.targetURL) + assert.Equal(t, tt.httpCode, w.Code, tt.name) + assert.Equal(t, tt.httpBody, w.Body.String(), tt.name) + } +} diff --git a/example/embed/data/server/index.html b/example/embed/data/server/index.html new file mode 100644 index 0000000..1d3fa49 --- /dev/null +++ b/example/embed/data/server/index.html @@ -0,0 +1 @@ +

Hello Embed

\ No newline at end of file diff --git a/example/embed/example.go b/example/embed/example.go new file mode 100644 index 0000000..6df55ce --- /dev/null +++ b/example/embed/example.go @@ -0,0 +1,27 @@ +package main + +import ( + "embed" + "fmt" + "net/http" + + "github.com/gin-contrib/static" + "github.com/gin-gonic/gin" +) + +//go:embed data +var server embed.FS + +func main() { + r := gin.Default() + r.Use(static.Serve("/", static.EmbedFolder(server, "data/server"))) + r.GET("/ping", func(c *gin.Context) { + c.String(200, "test") + }) + r.NoRoute(func(c *gin.Context) { + fmt.Printf("%s doesn't exists, redirect on /\n", c.Request.URL.Path) + c.Redirect(http.StatusMovedPermanently, "/") + }) + // Listen and Server in 0.0.0.0:8080 + r.Run(":8080") +} diff --git a/static.go b/local_file.go similarity index 56% rename from static.go rename to local_file.go index ffa00ee..f67f08d 100644 --- a/static.go +++ b/local_file.go @@ -11,11 +11,6 @@ import ( const INDEX = "index.html" -type ServeFileSystem interface { - http.FileSystem - Exists(prefix string, path string) bool -} - type localFileSystem struct { http.FileSystem root string @@ -50,21 +45,3 @@ func (l *localFileSystem) Exists(prefix string, filepath string) bool { } return false } - -func ServeRoot(urlPrefix, root string) gin.HandlerFunc { - return Serve(urlPrefix, LocalFile(root, false)) -} - -// Static returns a middleware handler that serves static files in the given directory. -func Serve(urlPrefix string, fs ServeFileSystem) gin.HandlerFunc { - fileserver := http.FileServer(fs) - if urlPrefix != "" { - fileserver = http.StripPrefix(urlPrefix, fileserver) - } - return func(c *gin.Context) { - if fs.Exists(urlPrefix, c.Request.URL.Path) { - fileserver.ServeHTTP(c.Writer, c.Request) - c.Abort() - } - } -} diff --git a/local_file_test.go b/local_file_test.go new file mode 100644 index 0000000..0ab922a --- /dev/null +++ b/local_file_test.go @@ -0,0 +1,34 @@ +package static + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +func TestLocalFile(t *testing.T) { + // SETUP file + testRoot, _ := os.Getwd() + f, err := ioutil.TempFile(testRoot, "") + if err != nil { + t.Error(err) + } + defer os.Remove(f.Name()) + f.WriteString("Gin Web Framework") + f.Close() + + dir, filename := filepath.Split(f.Name()) + router := gin.New() + router.Use(Serve("/", LocalFile(dir, true))) + + w := PerformRequest(router, "GET", "/"+filename) + assert.Equal(t, w.Code, 200) + assert.Equal(t, w.Body.String(), "Gin Web Framework") + + w = PerformRequest(router, "GET", "/") + assert.Contains(t, w.Body.String(), `