Golang HTTP FileServer 簡介

Posted on  Jun 4, 2018  in  Go 程式設計 - 初階  by  Amo Chen  ‐ 2 min read

Golang 內建提供 http.FileServer ,可以方便地透過 HTTP 存取檔案系統(file system),例如以下程式碼執行之後,就可以透過瀏覽器打開網址 http://localhost:8080 瀏覽 /usr/share/doc 資料夾內的檔案。

package main

import (
	"net/http"
)

func main() {
	http.ListenAndServe(":8080", http.FileServer(http.Dir("/usr/share/doc")))
}

也因為很方便,用 Golang 建構 web application 時,也可以利用 FileServer 幫忙處理靜態檔案(static files, 例如 CSS / JS / IMAGES)等,其範例如下:

package main

import (
	"net/http"
)

func main() {
    fs := http.FileServer(http.Dir("./static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

上述範例先使用 http.FileServer 建立當前目錄下的 static 資料夾的 FileServer Handler ,然後再利用 http.Handle 將所有路徑請求是 /static/ 開頭的 HTTP request 先利用 http.StripPrefix 將路徑的 /static/ 字串去除後,轉交給 FileServer Handler 處理。

因此假設有一個 HTTP request 的路徑是 /static/index.js 的話,就會被換成 index.js 然後交給 FileServer Handler 處理,而 FileServer Handler 會試圖取得 ./static/index.js 的內容回應,如果檔案不存在的話,就會回應 404 NOT FOUND 。

這邊要注意的是 FileServer 是可以存取資料夾的,因此假設上述範例 HTTP request 的路徑是 /static/ 的話,就會變成直接列出當前目錄下的 ./static 資料夾所有的檔案(如果你使用的是 http.Dir("/") 那就會整個作業系統的檔案被看光光,十分危險,千萬別這麼做,母湯母湯)。

要解決可以存取資料夾的問題的最簡單方法,就是在每個資料夾底下新增一個 index.html 即可。

As a special case, the returned file server redirects any request ending in “/index.html” to the same path, without the final “index.html”.

這種解決辦法適用於只有資料夾結構十分單純的情況下,如果資料夾內有多個資料夾,或者資料夾的結構很複雜,使用這種方式就相當辛苦,此時可以考慮將 FileServer 再包裝一層,解決此一困擾。以下是包裝過 FileServer 範例:

package main

import (
	"log"
	"net/http"
)

type securedFileSystem struct {
	fs http.FileSystem
}

func (sfs securedFileSystem) Open(path string) (http.File, error) {
	f, err := sfs.fs.Open(path)
	if err != nil {
		return nil, err
	}

	s, err := f.Stat()
	if s.IsDir() {
		index := strings.TrimSuffix(path, "/") + "/index.html"
		if _, err := sfs.fs.Open(index); err != nil {
			return nil, err
		}
	}

	return f, nil
}

func main() {
	fs := http.FileServer(securedFileSystem{http.Dir("./static")})
	http.Handle("/static/", http.StripPrefix("/static/", fs))
	err := http.ListenAndServe(":9090", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

上述範例最主要的關鍵部分在於實作 http.FileSystem 裡的 Open ,先讓 FileSytem 打開檔案,打開後判斷是否是資料夾,如果是資料夾,進一步看是否有 index.html 存在於檔案中,如果有的話就回傳 http.File ,沒有的話就回傳 err 。利用再包裝的方式達到阻擋存取資料夾的目的。

以上為本篇 http.FileServer 的介紹。

References

https://golang.org/pkg/net/http/#FileSystem

https://www.alexedwards.net/blog/disable-http-fileserver-directory-listings

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!