如何使用Gin从动态子目录中提供文件?

huangapple go评论73阅读模式
英文:

How to serve files from dynamic subdirectories using Gin?

问题

我有一个目录结构,像这样:

- uploads
    - rooms
        - 1
            - a.jpg
            - b.jpg
        - 2
            - a.jpg
            - c.jpg

rooms 目录中的子目录(1、2、...)是动态添加的。我想在 Gin 中创建一个端点,当给定确切的路径时,可以提供所有这些文件,但不公开文件/文件夹的列表。

例如,
http://localhost:8080/media/1/a.jpg 应该返回一张图片。但是 http://localhost:8080/media/1 应该返回 404。

英文:

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.

答案1

得分: 1

使用gin.Dir,并将listDirectory设置为false它也适用于子目录。

Dir返回一个可以被http.FileServer()使用的http.Filesystem。它在router.Static()内部使用。如果listDirectorytrue,则与http.Dir()的功能相同,否则返回一个阻止http.FileServer()列出目录文件的文件系统。

示例用法:

本地文件系统

/
|_ 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请求

GET: <host:port>/foo/1/foo.jpg

作为替代方案,您还可以声明一个带有路径参数的路由,并直接提供内容:

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)
})

通过检查修剪后的通配符路径*asset是否为空,您可以防止查询例如/media/1/(带有最后一个斜杠)以列出目录。而/media/1(不带最后一个斜杠)不匹配任何路由(它应该自动重定向到/media/1/)。

英文:

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(&quot;/foo&quot;, gin.Dir(&quot;static&quot;, false))

HTTP request

GET: &lt;host:port&gt;/foo/1/foo.jpg

<hr>

As an alternative, you can also declare a route with path params, and just serve the content:

	r.GET(&quot;/media/:dir/*asset&quot;, func(c *gin.Context) {
		dir := c.Param(&quot;dir&quot;)
		asset := c.Param(&quot;asset&quot;)
        if strings.TrimPrefix(asset, &quot;/&quot;) == &quot;&quot; {
			c.AbortWithStatus(http.StatusNotFound)
			return
		}
		fullName := filepath.Join(dir, filepath.FromSlash(path.Clean(&quot;/&quot;+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/).

huangapple
  • 本文由 发表于 2021年9月4日 03:09:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/69049626.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定