英文:
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/
).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论