英文:
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()内部使用。如果listDirectory为true,则与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("/foo", gin.Dir("static", false))
HTTP request
GET: <host:port>/foo/1/foo.jpg
<hr>
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/).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论