feat: add mobile transfer and drag upload

This commit is contained in:
OpenClaw Agent
2026-09-22 02:14:26 +08:00
parent 715904ab9f
commit 70231f9cf3
4 changed files with 108 additions and 4 deletions
+97
View File
@@ -7,6 +7,7 @@ import (
_ "embed"
"encoding/json"
"fmt"
"github.com/skip2/go-qrcode"
"html"
"image"
_ "image/gif"
@@ -85,6 +86,8 @@ func (s *Server) Handler() http.Handler {
mux.HandleFunc("/chfs/upload2", s.upload2)
mux.HandleFunc("/chfs/search", s.search)
mux.HandleFunc("/chfs/downloaddir/", s.downloadDir)
mux.HandleFunc("/chfs/downloadzip", s.downloadSelectedZip)
mux.HandleFunc("/chfs/qrcode", s.qrcode)
mux.HandleFunc("/chfs/shared", s.shared)
mux.HandleFunc("/chfs/shared/", s.shared)
mux.HandleFunc("/chfs/preview", s.preview)
@@ -1093,6 +1096,100 @@ func (s *Server) serveFile(w http.ResponseWriter, r *http.Request, p string, att
http.ServeContent(w, r, fi.Name(), fi.ModTime(), f)
}
func (s *Server) qrcode(w http.ResponseWriter, r *http.Request) {
u := r.URL.Query().Get("url")
if u == "" || len(u) > 4096 {
writeJSONError(w, http.StatusBadRequest, "invalid url")
return
}
data, err := qrcode.Encode(u, qrcode.Medium, 320)
if err != nil {
writeJSONError(w, http.StatusInternalServerError, "qrcode failed")
return
}
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Content-Type", "image/png")
_, _ = w.Write(data)
}
func (s *Server) downloadSelectedZip(w http.ResponseWriter, r *http.Request) {
a := account(r, s)
paths := r.URL.Query()["path"]
if len(paths) == 0 || len(paths) > 100 {
writeJSONError(w, http.StatusBadRequest, "invalid selection")
return
}
seen := map[string]bool{}
for i, raw := range paths {
p := strings.Trim(strings.ReplaceAll(raw, "\\", "/"), "/")
if p == "" || unsafePath(p) || seen[p] {
writeJSONError(w, http.StatusBadRequest, "invalid selection")
return
}
seen[p] = true
paths[i] = p
if !a.MaskFor(p).HasRead() {
writeJSONError(w, http.StatusForbidden, "has no privilege")
return
}
if _, err := s.Store.Stat(p); err != nil {
http.NotFound(w, r)
return
}
}
w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", `attachment; filename="download.zip"`)
zw := zip.NewWriter(w)
defer zw.Close()
added := map[string]bool{}
var walk func(string, string) error
walk = func(p, prefix string) error {
fi, err := s.Store.Stat(p)
if err != nil {
return err
}
if fi.IsDir() {
items, err := s.Store.List(p)
if err != nil {
return err
}
for _, it := range items {
child := path.Join(p, it.Name())
if !a.MaskFor(child).HasRead() {
continue
}
if err := walk(child, path.Join(prefix, it.Name())); err != nil {
return err
}
}
return nil
}
if added[prefix] {
return nil
}
added[prefix] = true
f, err := s.Store.Open(p)
if err != nil {
return err
}
defer f.Close()
h := &zip.FileHeader{Name: prefix, Method: zip.Deflate}
h.SetModTime(fi.ModTime())
wz, err := zw.CreateHeader(h)
if err != nil {
return err
}
_, err = io.Copy(wz, f)
return err
}
for _, p := range paths {
if err := walk(p, path.Base(p)); err != nil {
return
}
}
}
func (s *Server) downloadDir(w http.ResponseWriter, r *http.Request) {
a := account(r, s)
rawPath := strings.TrimPrefix(r.URL.Path, "/chfs/downloaddir/")