From f8e4a84d5373d931c6679a43dec7b9ba841c9519 Mon Sep 17 00:00:00 2001 From: Snowykami Date: Sat, 20 Sep 2025 12:45:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E9=A9=B1=E5=8A=A8=E5=92=8C=E6=9C=AC=E5=9C=B0=E9=A9=B1=E5=8A=A8?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=EF=BC=8C=E6=94=AF=E6=8C=81=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/repo/file.go | 1 + pkg/filedriver/base_driver.go | 38 +++++++++++++++++ pkg/filedriver/local_driver.go | 74 ++++++++++++++++++++++++++++++++ pkg/filedriver/webdav_driver.go | 76 +++++++++++++++++++++++++++++++++ pkg/utils/file.go | 1 + 5 files changed, 190 insertions(+) create mode 100644 internal/repo/file.go create mode 100644 pkg/filedriver/base_driver.go create mode 100644 pkg/filedriver/local_driver.go create mode 100644 pkg/filedriver/webdav_driver.go create mode 100644 pkg/utils/file.go diff --git a/internal/repo/file.go b/internal/repo/file.go new file mode 100644 index 0000000..e0281bf --- /dev/null +++ b/internal/repo/file.go @@ -0,0 +1 @@ +package repo diff --git a/pkg/filedriver/base_driver.go b/pkg/filedriver/base_driver.go new file mode 100644 index 0000000..8cb2869 --- /dev/null +++ b/pkg/filedriver/base_driver.go @@ -0,0 +1,38 @@ +package filedriver + +import ( + "fmt" + "github.com/LiteyukiStudio/spage/pkg/constants" + "github.com/cloudwego/hertz/pkg/app" + "io" + "os" +) + +type FileDriver interface { + Save(ctx *app.RequestContext, path string, r io.Reader) error + Open(ctx *app.RequestContext, path string) (io.ReadCloser, error) + Delete(ctx *app.RequestContext, path string) error + Stat(ctx *app.RequestContext, path string) (os.FileInfo, error) + Get(ctx *app.RequestContext, path string) + ListDir(ctx *app.RequestContext, path string) ([]os.FileInfo, error) +} + +type DriverConfig struct { + Type string `mapstructure:"file.driver.type"` + BasePath string `mapstructure:"file.driver.base_path"` + WebDavUrl string `mapstructure:"file.driver.webdav.url"` + WebDavUser string `mapstructure:"file.driver.webdav.user"` + WebDavPassword string `mapstructure:"file.driver.webdav.password"` + WebDavPolicy string `mapstructure:"file.driver.webdav.policy"` // proxy|redirect +} + +func GetFileDriver(driverConfig *DriverConfig) (FileDriver, error) { + switch driverConfig.Type { + case constants.FileDriverLocal: + return NewLocalDriver(driverConfig), nil + case constants.FileDriverWebdav: + return NewWebDAVClientDriver(driverConfig), nil + default: + return nil, fmt.Errorf("unsupported file driver type: %s", driverConfig.Type) + } +} diff --git a/pkg/filedriver/local_driver.go b/pkg/filedriver/local_driver.go new file mode 100644 index 0000000..c5486e0 --- /dev/null +++ b/pkg/filedriver/local_driver.go @@ -0,0 +1,74 @@ +package filedriver + +import ( + "github.com/cloudwego/hertz/pkg/app" + "io" + "os" + "path/filepath" +) + +type LocalDriver struct { + BasePath string +} + +func NewLocalDriver(driverConfig *DriverConfig) *LocalDriver { + return &LocalDriver{ + BasePath: driverConfig.BasePath, + } +} + +func (d *LocalDriver) fullPath(path string) string { + return filepath.Join(d.BasePath, path) +} + +func (d *LocalDriver) Save(ctx *app.RequestContext, path string, r io.Reader) error { + full := d.fullPath(path) + if err := os.MkdirAll(filepath.Dir(full), 0755); err != nil { + return err + } + f, err := os.Create(full) + if err != nil { + return err + } + defer func(f *os.File) { + err := f.Close() + if err != nil { + + } + }(f) + _, err = io.Copy(f, r) + return err +} + +func (d *LocalDriver) Open(ctx *app.RequestContext, path string) (io.ReadCloser, error) { + return os.Open(d.fullPath(path)) +} + +func (d *LocalDriver) Get(ctx *app.RequestContext, path string) { + ctx.File(d.fullPath(path)) + ctx.Abort() +} + +func (d *LocalDriver) Delete(ctx *app.RequestContext, path string) error { + return os.Remove(d.fullPath(path)) +} + +func (d *LocalDriver) Stat(ctx *app.RequestContext, path string) (os.FileInfo, error) { + return os.Stat(d.fullPath(path)) +} + +func (d *LocalDriver) ListDir(ctx *app.RequestContext, path string) ([]os.FileInfo, error) { + entries, err := os.ReadDir(d.fullPath(path)) + if err != nil { + return nil, err + } + infos := make([]os.FileInfo, 0, len(entries)) + for _, entry := range entries { + info, err := entry.Info() + if err != nil { + return nil, err + } + infos = append(infos, info) + } + return infos, nil +} diff --git a/pkg/filedriver/webdav_driver.go b/pkg/filedriver/webdav_driver.go new file mode 100644 index 0000000..2ea90f5 --- /dev/null +++ b/pkg/filedriver/webdav_driver.go @@ -0,0 +1,76 @@ +package filedriver + +import ( + "bytes" + "fmt" + "github.com/LiteyukiStudio/spage/pkg/constants" + "github.com/LiteyukiStudio/spage/pkg/resps" + "github.com/cloudwego/hertz/pkg/app" + "io" + "os" + "path" + + "github.com/studio-b12/gowebdav" +) + +type WebDAVClientDriver struct { + client *gowebdav.Client + config *DriverConfig + BasePath string +} + +func NewWebDAVClientDriver(driverConfig *DriverConfig) *WebDAVClientDriver { + c := gowebdav.NewClient(driverConfig.WebDavUrl, driverConfig.WebDavUser, driverConfig.WebDavPassword) + return &WebDAVClientDriver{client: c, BasePath: driverConfig.BasePath, config: driverConfig} +} + +func (d *WebDAVClientDriver) fullPath(p string) string { + if d.BasePath == "" { + return p + } + return path.Join(d.BasePath, p) +} + +func (d *WebDAVClientDriver) Save(ctx *app.RequestContext, p string, r io.Reader) error { + data, err := io.ReadAll(r) + if err != nil { + return err + } + return d.client.Write(d.fullPath(p), data, 0644) +} + +func (d *WebDAVClientDriver) Open(ctx *app.RequestContext, p string) (io.ReadCloser, error) { + data, err := d.client.Read(d.fullPath(p)) + if err != nil { + return nil, err + } + return io.NopCloser(bytes.NewReader(data)), nil +} + +func (d *WebDAVClientDriver) Get(ctx *app.RequestContext, p string) { + if d.config.WebDavPolicy == constants.WebDavPolicyRedirect { + ctx.Redirect(302, []byte(d.config.WebDavUrl+d.fullPath(p))) + return + } else { + data, err := d.client.Read(d.fullPath(p)) + if err != nil { + resps.InternalServerError(ctx, err.Error()) + return + } + ctx.SetBodyStream(bytes.NewReader(data), len(data)) + ctx.Response.Header.Set("Content-Type", "application/octet-stream") + ctx.Response.Header.Set("Content-Length", fmt.Sprintf("%d", len(data))) + } +} + +func (d *WebDAVClientDriver) Delete(ctx *app.RequestContext, p string) error { + return d.client.Remove(d.fullPath(p)) +} + +func (d *WebDAVClientDriver) Stat(ctx *app.RequestContext, p string) (os.FileInfo, error) { + return d.client.Stat(d.fullPath(p)) +} + +func (d *WebDAVClientDriver) ListDir(ctx *app.RequestContext, p string) ([]os.FileInfo, error) { + return d.client.ReadDir(d.fullPath(p)) +} diff --git a/pkg/utils/file.go b/pkg/utils/file.go new file mode 100644 index 0000000..d4b585b --- /dev/null +++ b/pkg/utils/file.go @@ -0,0 +1 @@ +package utils