Anup Kumar Panwar
Anup Kumar Panwar

Reputation: 65

How to serve files from dynamic subdirectories using Gin?

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.

Upvotes: 2

Views: 2778

Answers (1)

blackgreen
blackgreen

Reputation: 45081

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/).

Upvotes: 1

Related Questions