Go语言中间件框架 Negroni 的静态文件处理源码分析

Negroni是一个很是棒的中间件,尤为是其中间件调用链优雅的设计,以及对GO HTTP 原生处理器的兼容。我之前写过两篇文章,对Negroni进行了专门的分析,没有看过的朋友能够在看下。html

Go语言经典库使用分析(五)| Negroni 中间件(一) http://www.flysnow.org/2017/08/20/go-classic-libs-negroni-one.htmlgit

Go语言经典库使用分析(六)| Negroni 中间件(二) http://www.flysnow.org/2017/09/02/go-classic-libs-negroni-two.htmlgithub

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等。spa

从示例代码中,能够看到,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。

本文为原创文章,转载注明出处&&,欢迎扫码关注公众号 flysnow_org或者网站 http://www.flysnow.org/,&&第一时间看后续精彩文章。以为好的话,&&顺手分享到朋友圈吧,感谢支持。

以上这些是如何实现的呢?咱们看下静态文件处理的源代码,结合分析。

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.Copyhttp.ServeContent能够自动计算响应的内容长度、能够自动识别内容的MIME类型,还能够处理If-Match,If-Unmodified-Since,If-None-Match,If-Modified-Since和If-Range的要求。

由于* os.File实现了io.ReadSeeker接口,因此咱们能够直接使用文件的内容。

小结

好了,到了这里,咱们已经分析完了Negroni中静态文件处理中间件的实现,看完后相信你也能够写本身的静态文件处理服务了,能够本身试试,写一个和http.FileServer相似功能的静态文件处理服务。

本文为原创文章,转载注明出处,欢迎扫码关注公众号 flysnow_org或者网站 http://www.flysnow.org/,第一时间看后续精彩文章。以为好的话,顺手分享到朋友圈吧,感谢支持。

扫码关注

相关文章
相关标签/搜索