package main import ( "flag" "fmt" "log" "net/http" "os" "path/filepath" "strings" "text/template" _ "embed" ) type Entry struct { Name string IsDir bool } type Result struct { Err string Entries []Entry } //go:embed index.html var index string var tpl = template.Must(template.New("index.html").Parse(index)) func Render(w http.ResponseWriter, data interface{}) { w.Header().Set("Content-Type", "text/html;charset=utf8") if err := tpl.Execute(w, data); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } func View(w http.ResponseWriter, r *http.Request) { log.Println(r.URL.Path) path := filepath.Join(*dir, filepath.Clean(r.URL.Path)) if !strings.HasSuffix(r.URL.Path, "/") { w.Header().Add("Content-Disposition", fmt.Sprintf("attachment;filename=%s", filepath.Base(path))) http.ServeFile(w, r, path) return } entries, err := os.ReadDir(path) if err != nil { Render(w, &Result{Err: err.Error()}) return } items := make([]Entry, 0) for _, entry := range entries { items = append(items, Entry{ Name: entry.Name(), IsDir: entry.IsDir(), }) } Render(w, &Result{Entries: items}) } var ( dir = flag.String("dir", "./", "server file directory") listen = flag.String("listen", ":3000", "listen address") ) func main() { flag.Parse() http.HandleFunc("/", View) log.Printf("server listen at %s", *listen) log.Fatal(http.ListenAndServe(*listen, nil)) }