From ca7b21dc3e8acac02e6d3a8b6dab418ad6a16d82 Mon Sep 17 00:00:00 2001 From: xkeyC <3334969096@qq.com> Date: Mon, 27 Mar 2023 22:55:20 +0800 Subject: [PATCH 1/5] feat(Webdav):Add overwrite support for moveFiles and copyFiles --- pkg/webdav/file.go | 87 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/pkg/webdav/file.go b/pkg/webdav/file.go index 9957f65..9f7735d 100644 --- a/pkg/webdav/file.go +++ b/pkg/webdav/file.go @@ -38,26 +38,51 @@ func moveFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst fileIDs = []uint{src.(*model.File).ID} } - // 判断是否需要移动 - if src.GetPosition() != path.Dir(dst) { - err = fs.Move( - ctx, - folderIDs, - fileIDs, - src.GetPosition(), - path.Dir(dst), - ) - } - - // 判断是否需要重命名 - if err == nil && src.GetName() != path.Base(dst) { - err = fs.Rename( - ctx, - folderIDs, - fileIDs, - path.Base(dst), - ) - } + // 判断是否需要移动 + if src.GetPosition() != path.Dir(dst) { + err = fs.Move( + ctx, + folderIDs, + fileIDs, + src.GetPosition(), + path.Dir(dst), + ) + } + if overwrite { + if err := _checkOverwriteFile(ctx, fs, src, dst); err != nil { + return http.StatusNoContent, err + } + } + + // 判断是否需要移动 + if src.GetPosition() != path.Dir(dst) { + err = fs.Move( + ctx, + folderIDs, + fileIDs, + src.GetPosition(), + path.Dir(dst), + ) + } + + // 判断是否需要重命名 + if err == nil && src.GetName() != path.Base(dst) { + err = fs.Rename( + ctx, + folderIDs, + fileIDs, + path.Base(dst), + ) + } + // 判断是否需要重命名 + if err == nil && src.GetName() != path.Base(dst) { + err = fs.Rename( + ctx, + folderIDs, + fileIDs, + path.Base(dst), + ) + } if err != nil { return http.StatusInternalServerError, err @@ -74,6 +99,12 @@ func copyFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst } recursion++ + if overwrite { + if err := _checkOverwriteFile(ctx, fs, src, dst); err != nil { + return http.StatusNoContent, err + } + } + if src.IsDir() { err := fs.Copy( ctx, @@ -94,6 +125,22 @@ func copyFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst return http.StatusNoContent, nil } +// 判断目标 文件/夹 是否已经存在,存在则先删除目标文件/夹 +func _checkOverwriteFile(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst string) error { + if src.IsDir() { + ok, folder := fs.IsPathExist(dst) + if ok { + return fs.Delete(ctx, []uint{folder.ID}, []uint{}, false, false) + } + } else { + ok, file := fs.IsFileExist(dst) + if ok { + return fs.Delete(ctx, []uint{}, []uint{file.ID}, false, false) + } + } + return nil +} + // walkFS traverses filesystem fs starting at name up to depth levels. // // Allowed values for depth are 0, 1 or infiniteDepth. For each visited node, From cd9e9e25b931d7483f68430ff1901799090e0f51 Mon Sep 17 00:00:00 2001 From: xkeyC <3334969096@qq.com> Date: Tue, 28 Mar 2023 00:06:10 +0800 Subject: [PATCH 2/5] =?UTF-8?q?fix:=E4=BB=85=E5=9C=A8=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E6=97=B6=20overwrite?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/webdav/file.go | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/pkg/webdav/file.go b/pkg/webdav/file.go index 9f7735d..d353c9e 100644 --- a/pkg/webdav/file.go +++ b/pkg/webdav/file.go @@ -40,22 +40,11 @@ func moveFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst // 判断是否需要移动 if src.GetPosition() != path.Dir(dst) { - err = fs.Move( - ctx, - folderIDs, - fileIDs, - src.GetPosition(), - path.Dir(dst), - ) - } - if overwrite { - if err := _checkOverwriteFile(ctx, fs, src, dst); err != nil { - return http.StatusNoContent, err + if overwrite { + if err := _checkOverwriteFile(ctx, fs, src, dst); err != nil { + return http.StatusNoContent, err + } } - } - - // 判断是否需要移动 - if src.GetPosition() != path.Dir(dst) { err = fs.Move( ctx, folderIDs, @@ -65,15 +54,6 @@ func moveFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst ) } - // 判断是否需要重命名 - if err == nil && src.GetName() != path.Base(dst) { - err = fs.Rename( - ctx, - folderIDs, - fileIDs, - path.Base(dst), - ) - } // 判断是否需要重命名 if err == nil && src.GetName() != path.Base(dst) { err = fs.Rename( From e8e38029ca2f55329d9f49d48730322e88516894 Mon Sep 17 00:00:00 2001 From: xkeyC <3334969096@qq.com> Date: Tue, 28 Mar 2023 00:18:37 +0800 Subject: [PATCH 3/5] fix:error code --- pkg/webdav/file.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/webdav/file.go b/pkg/webdav/file.go index d353c9e..c9e17ed 100644 --- a/pkg/webdav/file.go +++ b/pkg/webdav/file.go @@ -42,7 +42,7 @@ func moveFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst if src.GetPosition() != path.Dir(dst) { if overwrite { if err := _checkOverwriteFile(ctx, fs, src, dst); err != nil { - return http.StatusNoContent, err + return http.StatusInternalServerError, err } } err = fs.Move( @@ -81,7 +81,7 @@ func copyFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst if overwrite { if err := _checkOverwriteFile(ctx, fs, src, dst); err != nil { - return http.StatusNoContent, err + return http.StatusInternalServerError, err } } From 42f7613bfae381e643eb7c19d2eccb1ba57fee7a Mon Sep 17 00:00:00 2001 From: xkeyC <3334969096@qq.com> Date: Wed, 29 Mar 2023 20:16:09 +0800 Subject: [PATCH 4/5] =?UTF-8?q?moveFiles=20=E4=BF=AE=E6=94=B9=E5=9B=9E?= =?UTF-8?q?=E6=97=A0=E6=9D=A1=E4=BB=B6=20overwrite=20=EF=BC=88Move=20?= =?UTF-8?q?=E6=88=96=20Rename=20=E9=83=BD=E4=BC=9A=E5=A4=84=E7=BD=9A?= =?UTF-8?q?=E5=86=B2=E7=AA=81=E9=97=AE=E9=A2=98=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/webdav/file.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/webdav/file.go b/pkg/webdav/file.go index c9e17ed..b36c1ed 100644 --- a/pkg/webdav/file.go +++ b/pkg/webdav/file.go @@ -38,13 +38,14 @@ func moveFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst fileIDs = []uint{src.(*model.File).ID} } + if overwrite { + if err := _checkOverwriteFile(ctx, fs, src, dst); err != nil { + return http.StatusInternalServerError, err + } + } + // 判断是否需要移动 if src.GetPosition() != path.Dir(dst) { - if overwrite { - if err := _checkOverwriteFile(ctx, fs, src, dst); err != nil { - return http.StatusInternalServerError, err - } - } err = fs.Move( ctx, folderIDs, From 1b4eff624dc39c104b635ae6de98ffd5f23b2d79 Mon Sep 17 00:00:00 2001 From: Weidi Deng Date: Fri, 7 Apr 2023 22:16:11 +0800 Subject: [PATCH 5/5] =?UTF-8?q?webdav=E5=85=BC=E5=AE=B9rclone=E7=9A=84next?= =?UTF-8?q?cloud=E9=80=89=E9=A1=B9(=E4=BF=AE=E6=94=B9=E6=97=A5=E6=9C=9F?= =?UTF-8?q?=E5=92=8Cchecksum)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/file.go | 2 ++ pkg/filesystem/hooks.go | 34 ++++++++++++++++++++ pkg/webdav/file.go | 70 ++++++++++++++++++++++------------------- pkg/webdav/prop.go | 44 ++++++++++++++++++++++++++ pkg/webdav/webdav.go | 3 ++ 5 files changed, 120 insertions(+), 33 deletions(-) diff --git a/models/file.go b/models/file.go index 788391c..fc14e31 100644 --- a/models/file.go +++ b/models/file.go @@ -44,6 +44,8 @@ const ( ThumbStatusMetadataKey = "thumb_status" ThumbSidecarMetadataKey = "thumb_sidecar" + + ChecksumMetadataKey = "webdav_checksum" ) func init() { diff --git a/pkg/filesystem/hooks.go b/pkg/filesystem/hooks.go index 2e887da..a2f9ed5 100644 --- a/pkg/filesystem/hooks.go +++ b/pkg/filesystem/hooks.go @@ -10,7 +10,10 @@ import ( "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/pkg/util" "io/ioutil" + "net/http" + "strconv" "strings" + "time" ) // Hook 钩子函数 @@ -268,3 +271,34 @@ func HookDeleteUploadSession(id string) Hook { return nil } } + +// NewWebdavAfterUploadHook 每次创建一个新的钩子函数 rclone 在 PUT 请求里有 OC-Checksum 字符串 +// 和 X-OC-Mtime +func NewWebdavAfterUploadHook(request *http.Request) func(ctx context.Context, fs *FileSystem, newFile fsctx.FileHeader) error { + var modtime time.Time + if timeVal := request.Header.Get("X-OC-Mtime"); timeVal != "" { + timeUnix, err := strconv.ParseInt(timeVal, 10, 64) + if err == nil { + modtime = time.Unix(timeUnix, 0) + } + } + checksum := request.Header.Get("OC-Checksum") + + return func(ctx context.Context, fs *FileSystem, newFile fsctx.FileHeader) error { + file := newFile.Info().Model.(*model.File) + if !modtime.IsZero() { + err := model.DB.Model(file).UpdateColumn("updated_at", modtime).Error + if err != nil { + return err + } + } + + if checksum != "" { + return file.UpdateMetadata(map[string]string{ + model.ChecksumMetadataKey: checksum, + }) + } + + return nil + } +} diff --git a/pkg/webdav/file.go b/pkg/webdav/file.go index 9957f65..6871bcd 100644 --- a/pkg/webdav/file.go +++ b/pkg/webdav/file.go @@ -38,26 +38,26 @@ func moveFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst fileIDs = []uint{src.(*model.File).ID} } - // 判断是否需要移动 - if src.GetPosition() != path.Dir(dst) { - err = fs.Move( - ctx, - folderIDs, - fileIDs, - src.GetPosition(), - path.Dir(dst), - ) - } - - // 判断是否需要重命名 - if err == nil && src.GetName() != path.Base(dst) { - err = fs.Rename( - ctx, - folderIDs, - fileIDs, - path.Base(dst), - ) - } + // 判断是否需要移动 + if src.GetPosition() != path.Dir(dst) { + err = fs.Move( + ctx, + folderIDs, + fileIDs, + src.GetPosition(), + path.Dir(dst), + ) + } + + // 判断是否需要重命名 + if err == nil && src.GetName() != path.Base(dst) { + err = fs.Rename( + ctx, + folderIDs, + fileIDs, + path.Base(dst), + ) + } if err != nil { return http.StatusInternalServerError, err @@ -74,21 +74,25 @@ func copyFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst } recursion++ + var ( + fileIDs []uint + folderIDs []uint + ) if src.IsDir() { - err := fs.Copy( - ctx, - []uint{src.(*model.Folder).ID}, - []uint{}, src.(*model.Folder).Position, - path.Dir(dst), - ) - if err != nil { - return http.StatusInternalServerError, err - } + folderIDs = []uint{src.(*model.Folder).ID} } else { - err := fs.Copy(ctx, []uint{}, []uint{src.(*model.File).ID}, src.(*model.File).Position, path.Dir(dst)) - if err != nil { - return http.StatusInternalServerError, err - } + fileIDs = []uint{src.(*model.File).ID} + } + + err = fs.Copy( + ctx, + folderIDs, + fileIDs, + src.GetPosition(), + path.Dir(dst), + ) + if err != nil { + return http.StatusInternalServerError, err } return http.StatusNoContent, nil diff --git a/pkg/webdav/prop.go b/pkg/webdav/prop.go index 5e3b4cc..8a6e562 100644 --- a/pkg/webdav/prop.go +++ b/pkg/webdav/prop.go @@ -16,9 +16,30 @@ import ( "strconv" "time" + model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" ) +type FileDeadProps struct { + *model.File +} + +// 实现 webdav.DeadPropsHolder 接口,不能在models.file里面定义 +func (file *FileDeadProps) DeadProps() (map[xml.Name]Property, error) { + return map[xml.Name]Property{ + xml.Name{Space: "http://owncloud.org/ns", Local: "checksums"}: { + XMLName: xml.Name{ + Space: "http://owncloud.org/ns", Local: "checksums", + }, + InnerXML: []byte("" + file.MetadataSerialized[model.ChecksumMetadataKey] + ""), + }, + }, nil +} + +func (file *FileDeadProps) Patch([]Proppatch) ([]Propstat, error) { + return nil, nil +} + type FileInfo interface { GetSize() uint64 GetName() string @@ -177,8 +198,18 @@ var liveProps = map[xml.Name]struct { // of one Propstat element. func props(ctx context.Context, fs *filesystem.FileSystem, ls LockSystem, fi FileInfo, pnames []xml.Name) ([]Propstat, error) { isDir := fi.IsDir() + if !isDir { + fi = &FileDeadProps{fi.(*model.File)} + } var deadProps map[xml.Name]Property + if dph, ok := fi.(DeadPropsHolder); ok { + var err error + deadProps, err = dph.DeadProps() + if err != nil { + return nil, err + } + } pstatOK := Propstat{Status: http.StatusOK} pstatNotFound := Propstat{Status: http.StatusNotFound} @@ -210,8 +241,18 @@ func props(ctx context.Context, fs *filesystem.FileSystem, ls LockSystem, fi Fil // Propnames returns the property names defined for resource name. func propnames(ctx context.Context, fs *filesystem.FileSystem, ls LockSystem, fi FileInfo) ([]xml.Name, error) { isDir := fi.IsDir() + if !isDir { + fi = &FileDeadProps{fi.(*model.File)} + } var deadProps map[xml.Name]Property + if dph, ok := fi.(DeadPropsHolder); ok { + var err error + deadProps, err = dph.DeadProps() + if err != nil { + return nil, err + } + } pnames := make([]xml.Name, 0, len(liveProps)+len(deadProps)) for pn, prop := range liveProps { @@ -219,6 +260,9 @@ func propnames(ctx context.Context, fs *filesystem.FileSystem, ls LockSystem, fi pnames = append(pnames, pn) } } + for pn := range deadProps { + pnames = append(pnames, pn) + } return pnames, nil } diff --git a/pkg/webdav/webdav.go b/pkg/webdav/webdav.go index b081932..054bced 100644 --- a/pkg/webdav/webdav.go +++ b/pkg/webdav/webdav.go @@ -389,6 +389,9 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *filesyst fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile) } + // rclone 请求 + fs.Use("AfterUpload", filesystem.NewWebdavAfterUploadHook(r)) + // 执行上传 err = fs.Upload(ctx, &fileData) if err != nil {