面向已经读了源码并且有做调试的读者,未读源码的看不了
这篇探讨一下serveFile的底层实现(注:重点文章,转载标明出处,谢谢!)
const indexPage = "/index.html"
// redirect .../index.html to .../
// can't use Redirect() because that would make the path absolute,
// which would be a problem running under StripPrefix
//假设用户输入的地址为:../static/abc/index.html
//包含index.html后缀,那么又重定向到../static/abc/
if strings.HasSuffix(r.URL.Path, indexPage) {
localRedirect(w, r, "./")
return
}
分析
如果请求路径包含index.html,就重定向到 ./。
举例,客户端发送请求../static/abc/index.html,那么重定向到../static/abc/。重定向怎么处理,参考下文。
f, err := fs.Open(name)
if err != nil {
msg, code := toHTTPError(err)
Error(w, msg, code)
return
}
defer f.Close()
d, err := f.Stat()
if err != nil {
msg, code := toHTTPError(err)
Error(w, msg, code)
return
}
分析
fs打开文件,再使用stat获取文件属性。
if redirect {
url := r.URL.Path
if d.IsDir() {
//举例,客户端发送请求.../static/cat,经过下面处理变成
//.../static/cat/
if url[len(url)-1] != '/' {
localRedirect(w, r, path.Base(url)+"/")
return
}
} else {
//举例,客户端发送请求.../static/cat.jpg/,经过处理重定向到../cat.jpg
if url[len(url)-1] == '/' {
localRedirect(w, r, "../"+path.Base(url))
return
}
}
}
分析
对于web请求来说,重定向都是true,具体情形如下:
if d.IsDir() {
url := r.URL.Path
// redirect if the directory name doesn't end in a slash
if url == "" || url[len(url)-1] != '/' {
localRedirect(w, r, path.Base(url)+"/")
return
}
// use contents of index.html for directory, if present
//举例,客户请求../cat/,结果组合成../cat/index.html
index := strings.TrimSuffix(name, "/") + indexPage
//打开目录下的index.html看是否存在?
ff, err := fs.Open(index)
if err == nil {
defer ff.Close()
dd, err := ff.Stat()
if err == nil {
name = index
d = dd //文件目录变成文件,属性发生变化
f = ff
}
}
}
分析
如果是目录,再看一下末尾是不是反斜线,如果不是继续重定向。
下面的代码将name和index.html进行组合,比如客户端请求../cat/,组合变成../cat/index.html,然后查看该文件是否存在。这段代码说明一个问题,当客户端请求目录时,首先选择输出该目录下的index.html,如果文件不存在才会将目录列表返回给客户端。
如果真有index.html,就将属性信息d赋值为dd,也就是从目录信息变成文件信息,用于下面的继续判断。
// Still a directory? (we didn't find an index.html file)
//如果依然没有查找到index.html文件
if d.IsDir() {
//查看是否已经修改
if checkIfModifiedSince(r, d.ModTime()) == condFalse {
writeNotModified(w)
return
}
//设置最后修改日期
setLastModified(w, d.ModTime())
//遍历文件夹,作为结果返回给前端
dirList(w, r, f)
return
}
分析
如果没有查找到index.html,说明d依然是目录。下面的处理逻辑如下:
// serveContent will check modification time
sizeFunc := func() (int64, error) { return d.Size(), nil }
serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f)
分析
如果不是目录,则进入文件的处理流程,执行serveContent。
这篇逐行解释了serveFile函数的执行,可以看出标准库写的非常好,值得反复阅读。
一、如果是目录,则可能需要重定向处理。处理完成之后,首先看目录下是否有index.html文件,如果有则直接返回;如果没有则返回目录文件列表
二、如果是文件,则进入serveContent处理。
因此serveFile主要做了两件事
页面更新:2024-03-29
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号