Skip to content

Commit

Permalink
feat(webdav): supoort rename in copy and move (#1774)
Browse files Browse the repository at this point in the history
  • Loading branch information
WeidiDeng authored Jul 18, 2023
1 parent f4a04ce commit ad6c6bc
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 17 deletions.
36 changes: 29 additions & 7 deletions models/folder.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ type Folder struct {
OwnerID uint `gorm:"index:owner_id"`

// 数据库忽略字段
Position string `gorm:"-"`
Position string `gorm:"-"`
WebdavDstName string `gorm:"-"`
}

// Create 创建目录
Expand Down Expand Up @@ -169,6 +170,11 @@ func (folder *Folder) MoveOrCopyFileTo(files []uint, dstFolder *Folder, isCopy b
oldFile.FolderID = dstFolder.ID
oldFile.UserID = dstFolder.OwnerID

// webdav目标名重置
if dstFolder.WebdavDstName != "" {
oldFile.Name = dstFolder.WebdavDstName
}

if err := DB.Create(&oldFile).Error; err != nil {
return copiedSize, err
}
Expand All @@ -177,16 +183,22 @@ func (folder *Folder) MoveOrCopyFileTo(files []uint, dstFolder *Folder, isCopy b
}

} else {
var updates = map[string]interface{}{
"folder_id": dstFolder.ID,
}
// webdav目标名重置
if dstFolder.WebdavDstName != "" {
updates["name"] = dstFolder.WebdavDstName
}

// 更改顶级要移动文件的父目录指向
err := DB.Model(File{}).Where(
"id in (?) and user_id = ? and folder_id = ?",
files,
folder.OwnerID,
folder.ID,
).
Update(map[string]interface{}{
"folder_id": dstFolder.ID,
}).
Update(updates).
Error
if err != nil {
return 0, err
Expand Down Expand Up @@ -221,6 +233,10 @@ func (folder *Folder) CopyFolderTo(folderID uint, dstFolder *Folder) (size uint6
// 顶级目录直接指向新的目的目录
if folder.ID == folderID {
newID = dstFolder.ID
// webdav目标名重置
if dstFolder.WebdavDstName != "" {
folder.Name = dstFolder.WebdavDstName
}
} else if IDCache, ok := newIDCache[*folder.ParentID]; ok {
newID = IDCache
} else {
Expand Down Expand Up @@ -282,15 +298,21 @@ func (folder *Folder) MoveFolderTo(dirs []uint, dstFolder *Folder) error {
return errors.New("cannot move a folder into itself")
}

var updates = map[string]interface{}{
"parent_id": dstFolder.ID,
}
// webdav目标名重置
if dstFolder.WebdavDstName != "" {
updates["name"] = dstFolder.WebdavDstName
}

// 更改顶级要移动目录的父目录指向
err := DB.Model(Folder{}).Where(
"id in (?) and owner_id = ? and parent_id = ?",
dirs,
folder.OwnerID,
folder.ID,
).Update(map[string]interface{}{
"parent_id": dstFolder.ID,
}).Error
).Update(updates).Error

return err

Expand Down
2 changes: 2 additions & 0 deletions pkg/filesystem/fsctx/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ const (
CancelFuncCtx
// 文件在从机节点中的路径
SlaveSrcPath
// Webdav目标名称
WebdavDstName
)
10 changes: 10 additions & 0 deletions pkg/filesystem/manage.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ func (fs *FileSystem) Copy(ctx context.Context, dirs, files []uint, src, dst str
// 记录复制的文件的总容量
var newUsedStorage uint64

// 设置webdav目标名
if dstName, ok := ctx.Value(fsctx.WebdavDstName).(string); ok {
dstFolder.WebdavDstName = dstName
}

// 复制目录
if len(dirs) > 0 {
subFileSizes, err := srcFolder.CopyFolderTo(dirs[0], dstFolder)
Expand Down Expand Up @@ -103,6 +108,11 @@ func (fs *FileSystem) Move(ctx context.Context, dirs, files []uint, src, dst str
return ErrPathNotExist
}

// 设置webdav目标名
if dstName, ok := ctx.Value(fsctx.WebdavDstName).(string); ok {
dstFolder.WebdavDstName = dstName
}

// 处理目录及子文件移动
err := srcFolder.MoveFolderTo(dirs, dstFolder)
if err != nil {
Expand Down
40 changes: 32 additions & 8 deletions pkg/webdav/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import (
"net/http"
"path"
"path/filepath"
"strconv"
"time"

model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
)

// slashClean is equivalent to but slightly more efficient than
Expand All @@ -23,6 +26,31 @@ func slashClean(name string) string {
return path.Clean(name)
}

// 更新Copy或Move后的修改时间
func updateCopyMoveModtime(req *http.Request, fs *filesystem.FileSystem, dst string) error {
var modtime time.Time
if timeVal := req.Header.Get("X-OC-Mtime"); timeVal != "" {
timeUnix, err := strconv.ParseInt(timeVal, 10, 64)
if err == nil {
modtime = time.Unix(timeUnix, 0)
}
}

if modtime.IsZero() {
return nil
}

ok, fi := isPathExist(req.Context(), fs, dst)
if !ok {
return nil
}

if fi.IsDir() {
return model.DB.Model(fi.(*model.Folder)).UpdateColumn("updated_at", modtime).Error
}
return model.DB.Model(fi.(*model.File)).UpdateColumn("updated_at", modtime).Error
}

// moveFiles moves files and/or directories from src to dst.
//
// See section 9.9.4 for when various HTTP status codes apply.
Expand All @@ -44,20 +72,17 @@ func moveFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst
}
}


// 判断是否需要移动
if src.GetPosition() != path.Dir(dst) {
err = fs.Move(
ctx,
context.WithValue(ctx, fsctx.WebdavDstName, path.Base(dst)),
folderIDs,
fileIDs,
src.GetPosition(),
path.Dir(dst),
)
}

// 判断是否需要重命名
if err == nil && src.GetName() != path.Base(dst) {
} else if src.GetName() != path.Base(dst) {
// 判断是否需要重命名
err = fs.Rename(
ctx,
folderIDs,
Expand All @@ -81,7 +106,6 @@ func copyFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst
}
recursion++


var (
fileIDs []uint
folderIDs []uint
Expand All @@ -100,7 +124,7 @@ func copyFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst
}

err = fs.Copy(
ctx,
context.WithValue(ctx, fsctx.WebdavDstName, path.Base(dst)),
folderIDs,
fileIDs,
src.GetPosition(),
Expand Down
22 changes: 20 additions & 2 deletions pkg/webdav/webdav.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,16 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *fil
return http.StatusBadRequest, errInvalidDepth
}
}
return copyFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") != "F", depth, 0)
status, err = copyFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") != "F", depth, 0)
if err != nil {
return status, err
}

err = updateCopyMoveModtime(r, fs, dst)
if err != nil {
return http.StatusInternalServerError, err
}
return status, nil
}

// windows下,某些情况下(网盘根目录下)Office保存文件时附带的锁token只包含源文件,
Expand All @@ -515,7 +524,16 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *fil
return http.StatusBadRequest, errInvalidDepth
}
}
return moveFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") == "T")
status, err = moveFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") == "T")
if err != nil {
return status, err
}

err = updateCopyMoveModtime(r, fs, dst)
if err != nil {
return http.StatusInternalServerError, err
}
return status, nil
}

// OK
Expand Down

0 comments on commit ad6c6bc

Please sign in to comment.