http.FileServer
方法屬於標準庫
net/http
,返回一個使用 FileSystem 介面 root 提供檔案訪問服務的 HTTP 處理器。可以方便的實現靜態檔案伺服器。
http.ListenAndServe(":8080", http.FileServer(http.Dir("/files/path")))
訪問 http://127.0.0.1:8080
,即可看到類似 Nginx 中 autoindex
瀏覽目錄功能。
源碼解析
我們現在開始將上述的那僅有的一行代碼進行剖析,看看到底是如何?的。源碼中英文注釋也比較詳細,可以參考。
我們先看 http.Dir()
,再看 http.FileServer()
,而 http.ListenAndServe()
監聽 TCP 通訊埠並提供路由服務,此處不贅述。
http.Dir()
從以下源碼我們可以看出,type Dir string
實現了 type FileSystem interface
的介面函數 Open
,http.Dir("/")
實際返回的是 http.Dir
類型,將字串路徑轉換成檔案系統。
// 所屬檔案: src/net/http/fs.go, 26-87行type Dir stringfunc (d Dir) Open(name string) (File, error) { // ...}type FileSystem interface { Open(name string) (File, error)}
http.FileServer()
http.FileServer()
方法返回的是 fileHandler
執行個體,而 fileHandler
結構體實現了 Handler
介面的方法 ServeHTTP()
。ServeHTTP
方法內的核心是 serveFile()
方法。
// 所屬檔案: src/net/http/fs.go, 690-716行type fileHandler struct { root FileSystem}func FileServer(root FileSystem) Handler { return &fileHandler{root}}func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) { upath := r.URL.Path if !strings.HasPrefix(upath, "/") { upath = "/" + upath r.URL.Path = upath } serveFile(w, r, f.root, path.Clean(upath), true)}
// 所屬檔案: src/net/http/server.go, 82行type Handler interface { ServeHTTP(ResponseWriter, *Request)}
serveFile()
方法判斷,如果訪問路徑是目錄,則列出目錄內容,如果是檔案則使用 serveContent()
方法輸出檔案內容。serveContent()
方法則是個讀取檔案內容並輸出的方法,此處不再貼代碼。
// 所屬檔案: src/net/http/fs.go, 540行// name is '/'-separated, not filepath.Separator.func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) { // 中間代碼已省略 if d.IsDir() { if checkIfModifiedSince(r, d.ModTime()) == condFalse { writeNotModified(w) return } w.Header().Set("Last-Modified", d.ModTime().UTC().Format(TimeFormat)) dirList(w, r, f) return } // serveContent will check modification time sizeFunc := func() (int64, error) { return d.Size(), nil } serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f)}
支援子目錄路徑
http.StripPrefix()
方法配合 http.Handle()
或 http.HandleFunc()
可以實現帶路由首碼的檔案服務。
package mainimport ( "net/http" "fmt")func main() { http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp")))) err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Println(err) }}
原文地址: https://shockerli.net/post/go...