feat: add copy directory
This commit is contained in:
parent
d3f307af6c
commit
6b32448a65
@ -142,5 +142,17 @@ func (s *Service) HandleCopyDirectory(
|
|||||||
req *httpserver.Request,
|
req *httpserver.Request,
|
||||||
resp *httpserver.Response,
|
resp *httpserver.Response,
|
||||||
) *httpserver.Response {
|
) *httpserver.Response {
|
||||||
|
params := req.Binded.(*CopyParams)
|
||||||
|
srcPath := utils.NormalizePath(params.SrcPath)
|
||||||
|
dstPath := utils.NormalizePath(params.DstPath)
|
||||||
|
isDir := params.IsDir
|
||||||
|
|
||||||
|
oldPath := utils.FullPath(srcPath)
|
||||||
|
newPath := utils.FullPath(dstPath)
|
||||||
|
if err := s.FileSystemManager.CopyDirectory(req.Context(), oldPath, newPath, isDir); err != nil {
|
||||||
|
logger.Error("copy dir %s to %s: %v", params.SrcPath, params.DstPath, err)
|
||||||
|
return resp.InternalServerError("copy dir is failed, " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
return resp.NoContent()
|
return resp.NoContent()
|
||||||
}
|
}
|
||||||
|
|||||||
131
engine/filesystem_copy.go
Normal file
131
engine/filesystem_copy.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"gosvc/logger"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"robotfs/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *FileSystemManager) CopyDirectory(ctx context.Context, oldPath, newPath utils.FullPath, isDir bool) error {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
|
||||||
|
if string(oldPath) == "/" {
|
||||||
|
return fmt.Errorf("cannot copy root directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDir && strings.HasPrefix(string(newPath), string(oldPath)) {
|
||||||
|
return fmt.Errorf("cannot copy directory to a subdirectory of itself")
|
||||||
|
}
|
||||||
|
|
||||||
|
oldEntry, err := f.FindEntry(ctx, oldPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s not found: %v", oldPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if newEntry, err := f.FindEntry(ctx, newPath); newEntry != nil && err == nil {
|
||||||
|
return fmt.Errorf("destination %s already exists", newPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
parentDir, _ := newPath.DirAndName()
|
||||||
|
if parentDir != "/" {
|
||||||
|
if parentEntry, _ := f.FindEntry(ctx, utils.FullPath(parentDir)); parentEntry == nil {
|
||||||
|
return fmt.Errorf("parent directory %s does not exist", parentDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := f.copyEntry(ctx, oldPath, oldEntry, newPath); err != nil {
|
||||||
|
return fmt.Errorf("copy metadata failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := utils.Clone(f.root, string(oldPath), string(newPath)); err != nil {
|
||||||
|
return fmt.Errorf("copy file data failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileSystemManager) copyEntry(ctx context.Context, oldPath utils.FullPath, entry *utils.Entry, newPath utils.FullPath) error {
|
||||||
|
if err := f.copySelfEntry(ctx, oldPath, entry, newPath, func() error {
|
||||||
|
if entry.IsDir {
|
||||||
|
if err := f.copyFolderSubEntries(ctx, oldPath, newPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("fail to copy %s => %s: %v", oldPath, newPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileSystemManager) copyFolderSubEntries(ctx context.Context, oldPath utils.FullPath, newPath utils.FullPath) error {
|
||||||
|
logger.Info("copying folder %s => %s", oldPath, newPath)
|
||||||
|
|
||||||
|
lastFileName := ""
|
||||||
|
includeLastFile := false
|
||||||
|
for {
|
||||||
|
entries := make([]*utils.Entry, 0, 1000)
|
||||||
|
lastFileName, err := f.doListDirectoryEntries(ctx, oldPath, lastFileName, includeLastFile, 1000, func(entry *utils.Entry) bool {
|
||||||
|
entries = append(entries, entry)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range entries {
|
||||||
|
itemOldPath := oldPath.Child(item.FullPath.Name())
|
||||||
|
itemNewPath := newPath.Child(item.FullPath.Name())
|
||||||
|
err := f.copyEntry(ctx, itemOldPath, item, itemNewPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastFileName == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileSystemManager) copySelfEntry(ctx context.Context, oldPath utils.FullPath, entry *utils.Entry, newPath utils.FullPath, copyFolderSubEntries func() error) error {
|
||||||
|
logger.Info("copying entry %s => %s", oldPath, newPath)
|
||||||
|
|
||||||
|
if oldPath == newPath {
|
||||||
|
logger.Info("skip copying entry %s => %s", oldPath, newPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newEntry := &utils.Entry{
|
||||||
|
FullPath: newPath,
|
||||||
|
IsDir: entry.IsDir,
|
||||||
|
Size: entry.Size,
|
||||||
|
CreateTime: time.Now().Unix(),
|
||||||
|
S3Key: entry.S3Key,
|
||||||
|
ContentType: entry.ContentType,
|
||||||
|
Etag: entry.Etag,
|
||||||
|
VersionID: entry.VersionID,
|
||||||
|
LastModificationTime: time.Now().Unix(),
|
||||||
|
Extended: entry.Extended,
|
||||||
|
}
|
||||||
|
|
||||||
|
if createErr := f.meta.InsertEntry(ctx, newEntry); createErr != nil {
|
||||||
|
return createErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if copyFolderSubEntries != nil {
|
||||||
|
if copyChildrenErr := copyFolderSubEntries(); copyChildrenErr != nil {
|
||||||
|
return copyChildrenErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -361,7 +361,7 @@ func (f *FileSystemManager) CopyFile(ctx context.Context, srcPath, dstPath utils
|
|||||||
return fmt.Errorf("find src file failed: %v", err)
|
return fmt.Errorf("find src file failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dstEntry, _ := f.FindEntry(ctx, dstPath); dstEntry != nil {
|
if dstEntry, err := f.FindEntry(ctx, dstPath); dstEntry != nil && err == nil {
|
||||||
return fmt.Errorf("dst file %s already exists", dstPath)
|
return fmt.Errorf("dst file %s already exists", dstPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -63,7 +63,6 @@ func (s *Service) RegisterRouteRules() {
|
|||||||
Handler: s.HandleRenameDirectory,
|
Handler: s.HandleRenameDirectory,
|
||||||
Bind: &RenameParams{},
|
Bind: &RenameParams{},
|
||||||
},
|
},
|
||||||
// Todo: juicefs clone
|
|
||||||
{
|
{
|
||||||
Path: "/dir/copy",
|
Path: "/dir/copy",
|
||||||
Method: http.MethodPost,
|
Method: http.MethodPost,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user