feat: add file copy and rename

This commit is contained in:
dukai 2025-05-22 18:28:36 +08:00
parent d83ad12683
commit f02b72e766
6 changed files with 87 additions and 36 deletions

View File

@ -30,6 +30,11 @@ else
GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o $(BINARY_NAME) ./ GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o $(BINARY_NAME) ./
endif endif
.PHONY: build-linux
build-linux:
@echo "Building for Linux..."
GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o $(BINARY_NAME) ./
.PHONY: clean .PHONY: clean
clean: clean:
rm -f $(BINARY_NAME) rm -f $(BINARY_NAME)
@ -41,4 +46,5 @@ help:
@echo " all - Generate protobuf files and build for current platform (default)" @echo " all - Generate protobuf files and build for current platform (default)"
@echo " proto - Generate protobuf files" @echo " proto - Generate protobuf files"
@echo " build - Build for current platform" @echo " build - Build for current platform"
@echo " build-linux - Build for linux platform"
@echo " clean - Remove built binaries and generated protobuf files" @echo " clean - Remove built binaries and generated protobuf files"

View File

@ -148,5 +148,16 @@ func (s *Service) HandleCopyFile(
req *httpserver.Request, req *httpserver.Request,
resp *httpserver.Response, resp *httpserver.Response,
) *httpserver.Response { ) *httpserver.Response {
params := req.Binded.(*CopyParams)
isDir := params.IsDir
srcPath := utils.FullPath(utils.NormalizePath(params.SrcPath))
dstPath := utils.FullPath(utils.NormalizePath(params.DstPath))
err := s.FileSystemManager.CopyFile(req.Context(), srcPath, dstPath, isDir)
if err != nil {
logger.Error("copy %s to %s: %v", srcPath, dstPath, err)
return resp.InternalServerError("copy file failed, " + err.Error())
}
return resp.NoContent() return resp.NoContent()
} }

View File

@ -29,7 +29,7 @@ func (e *Engine) Start() error {
if err := meta.Initialize(utils.GetViper(), "redis."); err != nil { if err := meta.Initialize(utils.GetViper(), "redis."); err != nil {
return fmt.Errorf("failed to initialize meta store: %v", err) return fmt.Errorf("failed to initialize meta store: %v", err)
} }
filesystem := NewFileSystemManager(meta, object) filesystem := NewFileSystemManager(meta, object, utils.GetViper().GetString("robotfs.data_path"))
e.FileSystemManager = filesystem e.FileSystemManager = filesystem
return nil return nil

View File

@ -15,12 +15,14 @@ type FileSystemManager struct {
sync.RWMutex sync.RWMutex
meta store.MetaStore meta store.MetaStore
storage *StorageManager storage *StorageManager
root string
} }
func NewFileSystemManager(meta store.MetaStore, storage *StorageManager) *FileSystemManager { func NewFileSystemManager(meta store.MetaStore, storage *StorageManager, root string) *FileSystemManager {
return &FileSystemManager{ return &FileSystemManager{
meta: meta, meta: meta,
storage: storage, storage: storage,
root: root,
} }
} }
@ -229,12 +231,15 @@ func (f *FileSystemManager) RenameFile(ctx context.Context, srcPath, dstPath uti
} }
} }
newS3Key := dstPath.ToS3Key() if isDir {
if err := f.storage.CopyObject(srcEntry.S3Key, newS3Key); err != nil { return fmt.Errorf("rename not file")
return fmt.Errorf("rename file failed: %v", err)
} }
newEntry := utils.NewFileEntry(dstPath, newS3Key, srcEntry.Size, srcEntry.ContentType, srcEntry.Etag, srcEntry.VersionID) if err := utils.Move(f.root, string(srcPath), string(dstPath)); err != nil {
return err
}
newEntry := utils.NewFileEntry(dstPath, dstPath.ToS3Key(), srcEntry.Size, srcEntry.ContentType, srcEntry.Etag, srcEntry.VersionID)
if err := f.meta.InsertEntry(ctx, newEntry); err != nil { if err := f.meta.InsertEntry(ctx, newEntry); err != nil {
return fmt.Errorf("insert new entry failed: %v", err) return fmt.Errorf("insert new entry failed: %v", err)
@ -244,8 +249,44 @@ func (f *FileSystemManager) RenameFile(ctx context.Context, srcPath, dstPath uti
return fmt.Errorf("delete source entry failed: %v", err) return fmt.Errorf("delete source entry failed: %v", err)
} }
if err := f.storage.DeleteObject(srcEntry.S3Key); err != nil { return nil
return fmt.Errorf("delete old file failed: %v", err) }
func (f *FileSystemManager) CopyFile(ctx context.Context, srcPath, dstPath utils.FullPath, isDir bool) error {
f.Lock()
defer f.Unlock()
if string(srcPath) == "/" || string(dstPath) == "/" {
return fmt.Errorf("cannot copy root")
}
srcEntry, err := f.FindEntry(ctx, srcPath)
if err != nil {
return fmt.Errorf("find src file failed: %v", err)
}
if dstEntry, _ := f.FindEntry(ctx, dstPath); dstEntry != nil {
return fmt.Errorf("dst file %s already exists", dstPath)
}
parentDir, _ := dstPath.DirAndName()
if parentDir != "/" {
if parentEntry, _ := f.FindEntry(ctx, utils.FullPath(parentDir)); parentEntry == nil {
return fmt.Errorf("parent directory %s does not exist", parentDir)
}
}
if isDir {
return fmt.Errorf("copy not file")
}
if err := utils.Clone(f.root, string(srcPath), string(dstPath)); err != nil {
return err
}
newEntry := utils.NewFileEntry(dstPath, dstPath.ToS3Key(), srcEntry.Size, srcEntry.ContentType, srcEntry.Etag, srcEntry.VersionID)
if err := f.meta.InsertEntry(ctx, newEntry); err != nil {
return fmt.Errorf("insert new entry failed: %v", err)
} }
return nil return nil

View File

@ -55,7 +55,12 @@ func NewStorageManager(s3Config *S3Config) (*StorageManager, error) {
s3Client := s3.New(awsSession) s3Client := s3.New(awsSession)
uploader := s3manager.NewUploader(awsSession) uploader := s3manager.NewUploader(awsSession, func(u *s3manager.Uploader) {
u.PartSize = 100 * 1024 * 1024
u.Concurrency = 5
u.LeavePartsOnError = false
u.BufferProvider = s3manager.NewBufferedReadSeekerWriteToPool(25 * 1024 * 1024)
})
downloader := s3manager.NewDownloader(awsSession) downloader := s3manager.NewDownloader(awsSession)
return &StorageManager{ return &StorageManager{
@ -185,24 +190,3 @@ func (sm *StorageManager) CopyObject(oldS3key, newS3Key string) error {
Key: aws.String(newS3Key), Key: aws.String(newS3Key),
}) })
} }
func (sm *StorageManager) ListObjects(prefix string) ([]string, error) {
var result []string
input := &s3.ListObjectsV2Input{
Bucket: aws.String(sm.bucketName),
Prefix: aws.String(prefix),
}
err := sm.s3Client.ListObjectsV2Pages(input, func(page *s3.ListObjectsV2Output, lastPage bool) bool {
for _, obj := range page.Contents {
result = append(result, *obj.Key)
}
return !lastPage
})
if err != nil {
return nil, err
}
return result, nil
}

View File

@ -5,8 +5,8 @@ import (
"os/exec" "os/exec"
) )
func CloneFile(src, dst string) error { func Clone(root, src, dst string) error {
cmd := exec.Command("juicefs", "clone", src, dst) cmd := exec.Command("juicefs", "clone", root+src, root+dst)
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return fmt.Errorf("clone failed: %v, output: %s", err, string(output)) return fmt.Errorf("clone failed: %v, output: %s", err, string(output))
@ -14,3 +14,12 @@ func CloneFile(src, dst string) error {
return nil return nil
} }
func Move(root, src, dst string) error {
cmd := exec.Command("mv", root+src, root+dst)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("move failed: %v, output: %s", err, string(output))
}
return nil
}