静态文件(如图片)的访问地址与实际存储的地址一般是不一样的。
例如在blog-service中文件的访问地址为/static/...
但是实际的文件存储地址为/storage/uploads
目录下。
那么gin是如何做到两个地址间的映射的?
我们做了什么
我们只需要在路由中注册StaticFS,告诉gin框架我们要将/static
和/storage/uploads
目录关联起来
func NewRouter() *gin.Engine {
...
r.POST("/upload/file", upload.UploadFile)
r.StaticFS("/static", http.Dir(global.AppSetting.UploadSavePath))
apiv1 := r.Group("/api/v1"){...}
return r
}
gin做了什么
查看StaticFS方法
func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes {
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
panic("URL parameters can not be used when serving a static folder")
}
handler := group.createStaticHandler(relativePath, fs)
urlPattern := path.Join(relativePath, "/*filepath")
group.GET(urlPattern, handler)
group.HEAD(urlPattern, handler)
return group.returnObj()
}
1.暴露的地址中禁止使用
*
和:
2.创建一个gin.HandlerFunc并注册到路由查看创建gin.HandlerFunc的方法为
createStaticHandler
func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
absolutePath := group.calculateAbsolutePath(relativePath)
fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
return func(c *Context) {
if _, noListing := fs.(*onlyFilesFS); noListing {
c.Writer.WriteHeader(http.StatusNotFound)
}
file := c.Param("filepath")
// Check if file exists and/or if we have permission to access it
f, err := fs.Open(file)
if err != nil {
c.Writer.WriteHeader(http.StatusNotFound)
c.handlers = group.engine.noRoute
// Reset index
c.index = -1
return
}
f.Close()
fileServer.ServeHTTP(c.Writer, c.Request)
}
}
1.http.StripPrefix
返回了一个http.HandlerFunc类型的函数(即一个Handler)
2.将http包下的http.HandlerFunc转换成了gin.HandlerFunc
总结:
http.StripPrefix创建了Handler
- 移除URL前缀,
- 调用http.FileServer(fs)的ServeHttp
封装为gin.HandlerFunc
在StaticFS方法里进行注册