http.FileServer()で階層ごと複数ファイルをサーブする【Go】
http.FileServer()関数を使って、階層ごと複数ファイルをサーブすることができます。
返り値の型はHandlerです。
よって、指定されたURLにアクセスされた際にHandlerを実行する、
http.Handle()関数の第2引数に指定することができます。
func http.FileServe(root FileSystem) Handler
引数はFileSystemであり、これは次のようなinterfaceです。
type FileSystem interface { Open(name string) (File, error) }
したがって、Open(name string) (File, error)が実装されているものはFileSystemと見なすことができます。
これに該当するのが、type Dirです。下記メソッドが定義されています。
func (d Dir) Open(name string) (File, error)
よって、http.Dirをhttp.FileServe()の引数とすることができます。
パスを指定するときは、次のように文字列をhttp.Dirにキャストします。
http.Dir(".") // 同階層のすべてのファイル、ディレクトリが対象
このように「.」を用いた、gitライクな階層指定が可能です。
以上を踏まえて、main.goを次のようにします。
func main() { http.Handle("/", http.FileServer(http.Dir("../sample"))) // 一つ上の階層にあるsampleディレクトリ以下すべてが対象 http.HandleFunc("/pict", pict) // HandleFuncで他のhandlerを追加 http.ListenAndServe(":8080", nil) } func pict(w http.ResponseWriter, req *http.Request) { f, err := os.Open("pict.png") if err != nil { http.Error(w, "file not found", 404) } defer f.Close() fi, err := f.Stat() if err != nil { http.Error(w, "file not found", 404) } http.ServeContent(w, req, fi.Name(), fi.ModTime(), f) }
一度成功したコードを修正して、他の階層を試そうとしても変わらない場合は、
キャッシュをクリアすると解決するかもしれません。
単一ファイルには、http.ServeFile()かhttp.ServeContent()を使います。
http.ServeContent()【Go】 - 技術向上
http.FileServer()には、一つ問題があります。
http.Handle("/abc", http.FileServer(http.Dir("../sample")))
というように、http.Handle()の第1引数で階層を特定した場合に、
http.FileServer()は「http.Dirにキャストしたパス + http.Handle()の第1引数」を捜します。
つまり、sampleの下にabcという階層を用意する必要があります。これでは不便です。
この問題を解決する方法を下記で紹介しています。
http.StripPrefix()でフォルダ階層とURLの指定を分離する【Go】 - 技術向上