洞察探讨小游戏SDK接入的最佳实践以及对企业跨平台开发的优势
1010
2022-08-31
Go语言中间件框架 Negroni 的静态文件处理源码分析(golang中间件开发)
Negroni Static中间件
Negroni设计的时候,内置了3个中间件,它们分别是Logger、Recovery和Static,用于日志处理,故障恢复以及静态文件处理,今天来分析下静态文件处理的源码实现。
Negroni的静态文件处理中间件,主要是为了把我们电脑(服务器)上的文件托管到Web服务上,可以通过HTTP的方式访问查看对应的电脑上的文件。如果请求的文件不存在,那么请求将会转给下一个中间件;如果文件存在,那么就会显示静态文件的内容,请求到此终止。
使用Negroni实现静态处理非常简单,我们看一个示例。
package main import (
"github.com/urfave/negroni" "net/http" ) func main(){
n := negroni.New()
n.Use(negroni.NewLogger())
n.Use(negroni.NewStatic(http.Dir("/tmp")))
n.Run(":8080")
}
示例中,把/tmp目录下的文件,托管在了Web服务器上,可以通过HTTP的方式访问到。假如/tmp下有一个1.txt文件,那么我们打开浏览器,输入地址http://127.0.0.1:8080/1.txt即可访问对应的1.txt文件的内容。
示例中,我添加了一个n.Use(negroni.NewLogger())中间件,用于打印访问日志,比如请求的URL等。
从示例代码中,可以看到,Negroni主要通过negroni.NewStatic函数,生成一个静态文件处理的中间件。
// NewStatic returns a new instance of Static func NewStatic(directory http.FileSystem) *Static {
return &Static{
Dir: directory,
Prefix: "",
IndexFile: "index.html",
}
}
该函数接受一个http.FileSystem类型的参数,用于指定要处理的目录。从函数的源代码可以看出,返回的其实是一个*Static,这个Static结构体有三个字段。
Negroni Static 结构体分析
type Static struct {
// 静态服务要处理目录 Dir http.FileSystem
// 给这些静态文件添加的URL前缀,主要用于对处理的静态文件分类 Prefix string // 索引文件,访问Dir目录的时候,显示这个索引文件的内容 IndexFile string }
第一个字段我们已经在示例中演示了,下面看看另外两个字段的用法,我通过示例说明,更容易理解。
Negroni Static Prefix 分析
现在我要托管/tmp目录下的文件,但是我又想通过http://hostname/tmp/1.txt这样的方式进行归类,让访问的人知道,这些文件是在tmp/下的,这就可以用到Prefix字段了。我们把原来的示例修改下看看。
func main(){
n := negroni.New()
n.Use(negroni.NewLogger())
n.Use(&negroni.Static{
Dir: http.Dir("/tmp"),
Prefix: "/tmp",
IndexFile: "index.html",
})
n.Run(":8080")
}
我们不再使用NewStatic方法,而是直接通过&negroni.Static{}构建一个静态文件处理的中间件,在构建的时候,指定Prefix的值为/tmp,现在我们再访问/tmp下的1.txt文件,就只能通过http://127.0.0.1:8080/tmp/1.txt,而不是原来的http://127.0.0.1:8080/1.txt,这样我们通过给URL加前缀,达到文件分类的目的,其实就是增加一个URL path。
以上这些是如何实现的呢?我们看下静态文件处理的源代码,结合分析。
file := r.URL.Path
// if we have a prefix, filter requests by stripping the prefix if s.Prefix != "" {
if !strings.HasPrefix(file, s.Prefix) {
next(rw, r)
return }
file = file[len(s.Prefix):]
if file != "" && file[0] != '/' {
next(rw, r)
return }
}
f, err := s.Dir.Open(file)
以上这段代码,是通过URL路径,查找对应的静态文件的核心代码。从源代码中可以看到,对Prefix不为空的情况进行了特殊处理,如果Prefix不为空,那么我们就要从URL Path中去掉这个Prefix,因为Prefix是我们自己强加入的,不属于文件路径中的部分,所以要剥离掉,这样才可以得到要处理文件的真实路径,也就是源代码中的file变量,最后通过s.Dir.Open(file)打开文件,得到其内容显示即可。
Negroni Static IndexFile 分析
最后一个字段是IndexFile,用于指定索引文件。对于我们使用过Apache、Nginx的都比较熟悉,我们指定了index.html作为索引文件后,那么我们再访问http://127.0.0.1:8080/tmp/这个目录,显示的其实是index.html文件的内容,因为我们访问的其实是http://127.0.0.1:8080/tmp/index.html,这就是IndexFile的作用。
Negroni的静态处理器中间件源代码中,对于IndexFile也非常简洁,容易理解。
// try to serve index file if fi.IsDir() {
file = path.Join(file, s.IndexFile)
f, err = s.Dir.Open(file)
}
//省略了无关代码
这个源代码处理很简单,如果是一个目录的话,就把目录和IndexFile拼接成一个新的文件路径,进行二次打开。
如何在请求页面上显示文件内容
在一系列的打开、判断中,如果最后可以找到正确的文件,拿到内容,那么就可以把内容展示到浏览器的页面上了。
http.ServeContent(rw, r, file, fi.ModTime(), f)
非常简洁的一段代码,即达到了我们的目的,该函数可以把ReadSeeker中的内容,显示到请求的页面上。
func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) {
sizeFunc := func() (int64, error) {
size, err := content.Seek(0, io.SeekEnd)
if err != nil {
return 0, errSeeker
}
_, err = content.Seek(0, io.SeekStart)
if err != nil {
return 0, errSeeker
}
return size, nil }
serveContent(w, req, name, modtime, sizeFunc, content)
}
相比io.Copy,http.ServeContent可以自动计算响应的内容长度、可以自动识别内容的MIME类型,还可以处理If-Match,If-Unmodified-Since,If-None-Match,If-Modified-Since和If-Range的要求。
因为* os.File实现了io.ReadSeeker接口,所以我们可以直接使用文件的内容。
小结
好了,到了这里,我们已经分析完了Negroni中静态文件处理中间件的实现,看完后相信你也可以写自己的静态文件处理服务了,可以自己试试,写一个和http.FileServer类似功能的静态文件处理服务。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~