Go开发 文件服务fs.go深入探讨(三)第二层serveFile详解

面向已经读了源码并且有做调试的读者,未读源码的看不了

这篇探讨一下serveFile的底层实现(注:重点文章,转载标明出处,谢谢!

第1段 处理index.html

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/。重定向怎么处理,参考下文。

第2段 打开文件并获取信息

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获取文件属性。

第3段 重定向的处理

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,具体情形如下:

  1. 如果是目录,且末尾没有'/'则重定向一次。比如客户端发送../static/cat,那么重定向到../static/cat/,多加了一个斜线。
  2. 如果是文件则相反,把最后面的反斜线去掉,然后再重定向。比如../static/cat.jpg/重定向到../static/cat.jpg

第4段 目录Dir的处理

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,也就是从目录信息变成文件信息,用于下面的继续判断。

第5段 目录Dir的继续处理

// 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依然是目录。下面的处理逻辑如下:

  1. 查看目录的最后修改时间,是不是在请求头的最后修改时间之后,如果不是,说明不符号请求头条件,然后返回304.
  2. 重置最后修改时间
  3. 列举目录列表,然后返回给客户端。
  4. 流程结束。

第6段 执行serveContent

// 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主要做了两件事

  1. 打开了文件或目录
  2. 对目录的情形做了重点处理
展开阅读全文

页面更新:2024-03-29

标签:文件   组合   斜线   末尾   详解   源码   客户端   属性   时间   目录   信息

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top