From acadf65b9342ae85f2e5dd18cd073dbc684ada46 Mon Sep 17 00:00:00 2001 From: veypi Date: Mon, 21 Oct 2024 20:09:37 +0800 Subject: [PATCH] feat: webdav dir reander --- oa/builtin/webdav/dir.html | 133 ++++++++++++++++++++++++++++++++ oa/builtin/webdav/dir_render.go | 99 ++++++++++++++---------- oa/builtin/webdav/webdav.go | 2 +- 3 files changed, 193 insertions(+), 41 deletions(-) create mode 100644 oa/builtin/webdav/dir.html diff --git a/oa/builtin/webdav/dir.html b/oa/builtin/webdav/dir.html new file mode 100644 index 0000000..f17f6d2 --- /dev/null +++ b/oa/builtin/webdav/dir.html @@ -0,0 +1,133 @@ + + + + + + + OA + + + + +
+ /{{ range .path }}{{if .}}{{.}}/{{end}}{{end}} +
+
+
+
+ {{.cdir}} directory + {{.cfile}} files + {{.size}} +
+
+ {{ range .files }} +
+ +
{{index . 2}}
+
{{index . 3}}
+
+ {{end}} +
+
+
+
+ + + + + diff --git a/oa/builtin/webdav/dir_render.go b/oa/builtin/webdav/dir_render.go index 3fc20b8..d8905ef 100644 --- a/oa/builtin/webdav/dir_render.go +++ b/oa/builtin/webdav/dir_render.go @@ -8,11 +8,12 @@ package webdav import ( + "embed" "fmt" + "html/template" "io/fs" "net/http" "net/url" - "sort" "strings" "github.com/veypi/utils/logv" @@ -36,54 +37,72 @@ func (d dirEntryDirs) len() int { return len(d) } func (d dirEntryDirs) isDir(i int) bool { return d[i].IsDir() } func (d dirEntryDirs) name(i int) string { return d[i].Name() } -func dirList(w http.ResponseWriter, r *http.Request, f File) { +//go:embed dir.html +var dirTemplate embed.FS // 嵌入文件系统 + +func size2Label(s int64) string { + if s < 1024 { + return fmt.Sprintf("%d B", s) + } else if s < 1048576 { + return fmt.Sprintf("%d KB", s/1024) + } else if s < 1073741824 { + return fmt.Sprintf("%d MB", s/1024/1024) + } else { + return fmt.Sprintf("%d MB", s/1024/1024/1024) + } +} + +func dirList(w http.ResponseWriter, r *http.Request, f File, rootPath string) { // Prefer to use ReadDir instead of Readdir, // because the former doesn't require calling // Stat on every entry of a directory on Unix. - var dirs anyDirs var err error + defer func() { + if e := recover(); e != nil { + err = fmt.Errorf("%v", e) + } + if err != nil { + logv.Warn().Msgf("http: error reading directory: %v", err) + http.Error(w, "Error reading directory", http.StatusInternalServerError) + } + }() + dir_count := 0 + f_count := 0 + var file_bytes int64 + files := make([][4]any, 0, 5) + dirs := make([][4]any, 0, 5) if d, ok := f.(fs.ReadDirFile); ok { - var list dirEntryDirs - list, err = d.ReadDir(-1) - dirs = list + for _, item := range logv.AssertFuncErr(d.ReadDir(-1)) { + if item.IsDir() { + name := item.Name() + "/" + dir_count += 1 + fstat, _ := item.Info() + furl := url.URL{Path: name} + dirs = append(dirs, [4]any{name, furl.String(), "-----", fstat.ModTime().UTC()}) + } else { + name := item.Name() + f_count += 1 + fstat, _ := item.Info() + file_bytes += fstat.Size() + furl := url.URL{Path: name} + files = append(files, [4]any{name, furl.String(), size2Label(fstat.Size()), fstat.ModTime().UTC()}) + } + } } else { - var list fileInfoDirs - list, err = f.Readdir(-1) - dirs = list + for _, item := range logv.AssertFuncErr(d.ReadDir(-1)) { + name := item.Name() + f_count += 1 + fstat, _ := item.Info() + file_bytes += fstat.Size() + furl := url.URL{Path: name} + files = append(files, [4]any{name, furl.String(), size2Label(fstat.Size()), fstat.ModTime().UTC()}) + } } - if err != nil { - logv.Warn().Msgf("http: error reading directory: %v", err) - http.Error(w, "Error reading directory", http.StatusInternalServerError) - return - } - sort.Slice(dirs, func(i, j int) bool { return dirs.name(i) < dirs.name(j) }) + dirBody := logv.AssertFuncErr(dirTemplate.ReadFile("dir.html")) w.Header().Set("Content-Type", "text/html; charset=utf-8") - fmt.Fprintf(w, "\n") - fmt.Fprintf(w, "\n") - fmt.Fprintf(w, "
\n")
-	for i, n := 0, dirs.len(); i < n; i++ {
-		name := dirs.name(i)
-		if dirs.isDir(i) {
-			name += "/"
-		}
-		// name may contain '?' or '#', which must be escaped to remain
-		// part of the URL path, and not indicate the start of a query
-		// string or fragment.
-		url := url.URL{Path: name}
-		fmt.Fprintf(w, "%s\n", url.String(), htmlReplacer.Replace(name))
-	}
-	fmt.Fprintf(w, "
\n") + tpl := logv.AssertFuncErr(template.New("").Parse(string(dirBody))) + logv.AssertError(tpl.Execute(w, map[string]any{"files": append(dirs, files...), "path": strings.Split(rootPath, "/"), "cdir": dir_count, "cfile": f_count, "size": size2Label(file_bytes)})) } - -var htmlReplacer = strings.NewReplacer( - "&", "&", - "<", "<", - ">", ">", - // """ is shorter than """. - `"`, """, - // "'" is shorter than "'" and apos was not in HTML until HTML5. - "'", "'", -) diff --git a/oa/builtin/webdav/webdav.go b/oa/builtin/webdav/webdav.go index 71bf80a..7555c45 100644 --- a/oa/builtin/webdav/webdav.go +++ b/oa/builtin/webdav/webdav.go @@ -232,7 +232,7 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (sta } if fi.IsDir() { if h.EnableDirRender { - dirList(w, r, f) + dirList(w, r, f, r.URL.Path) return 0, nil } return http.StatusMethodNotAllowed, nil