1. <acronym id="pirhh"><pre id="pirhh"><dd id="pirhh"></dd></pre></acronym>
        1. <tt id="pirhh"><pre id="pirhh"><dd id="pirhh"></dd></pre></tt>
          <rt id="pirhh"></rt> <code id="pirhh"><object id="pirhh"></object></code>
            <listing id="pirhh"><object id="pirhh"><tr id="pirhh"></tr></object></listing>
            <code id="pirhh"></code>

            Go語言中間件框架 Negroni 的靜態文件處理源碼分析

            Negroni是一個非常棒的中間件,尤其是其中間件調用鏈優雅的設計,以及對GO HTTP 原生處理器的兼容。我以前寫過兩篇文章,對Negroni進行了專門的分析,沒有看過的朋友可以在看下。

            Go語言經典庫使用分析(五)| Negroni 中間件(一) http://www.flysnow.org/2017/08/20/go-classic-libs-negroni-one.html

            Go語言經典庫使用分析(六)| Negroni 中間件(二) http://www.flysnow.org/2017/09/02/go-classic-libs-negroni-two.html

            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。

            本文為原創文章,轉載注明出處&&,歡迎掃碼關注公眾號 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/ ,第一時間看后續精彩文章。覺得好的話,順手分享到朋友圈吧,感謝支持。

            我來評幾句
            登錄后評論

            已發表評論數()

            相關站點

            熱門文章
            天辰线上娱乐

            1. <acronym id="pirhh"><pre id="pirhh"><dd id="pirhh"></dd></pre></acronym>
                  1. <tt id="pirhh"><pre id="pirhh"><dd id="pirhh"></dd></pre></tt>
                    <rt id="pirhh"></rt> <code id="pirhh"><object id="pirhh"></object></code>
                      <listing id="pirhh"><object id="pirhh"><tr id="pirhh"></tr></object></listing>
                      <code id="pirhh"></code>

                      1. <acronym id="pirhh"><pre id="pirhh"><dd id="pirhh"></dd></pre></acronym>
                            1. <tt id="pirhh"><pre id="pirhh"><dd id="pirhh"></dd></pre></tt>
                              <rt id="pirhh"></rt> <code id="pirhh"><object id="pirhh"></object></code>
                                <listing id="pirhh"><object id="pirhh"><tr id="pirhh"></tr></object></listing>
                                <code id="pirhh"></code>