I have a directory structure like:
- uploads
- rooms
- 1
- a.jpg
- b.jpg
- 2
- a.jpg
- c.jpg
The subdirectories in the rooms
directory (1, 2, ...) are added dynamically. I want to create an endpoint in Gin
that can serve all these files when given the exact path but not expose the list of files/folders.
For example,
http://localhost:8080/media/1/a.jpg
should return an image. But http://localhost:8080/media/1
should return 404.
Use gin.Dir
with listDirectory == false
. It works on subdirectories too.
Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally in router.Static(). If listDirectory == true, then it works the same as http.Dir() otherwise it returns a filesystem that prevents http.FileServer() to list the directory files.
Example usage:
Local filesystem
/
|_ static
|_ 1
|_ foo.jpg
|_ bar.jpg
|_ main.go
|_ go.mod
|_ go.sum
main.go
r := gin.New()
r.StaticFS("/foo", gin.Dir("static", false))
HTTP request
GET: <host:port>/foo/1/foo.jpg
As an alternative, you can also declare a route with path params, and just serve the content:
r.GET("/media/:dir/*asset", func(c *gin.Context) {
dir := c.Param("dir")
asset := c.Param("asset")
if strings.TrimPrefix(asset, "/") == "" {
c.AbortWithStatus(http.StatusNotFound)
return
}
fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+asset)))
c.File(fullName)
})
By checking if the trimmed wildcard path *asset
is empty, you prevent queries to, e.g. /media/1/
(with final slash) to list the directory. Instead /media/1
(without final slash) doesn't match any route (it should automatically redirect to /media/1/
).