mirror of https://github.com/Xhofe/alist
feat: optimize file operation interface (#2757)
* feat: optimize file operation interface * chore: fix typo Co-authored-by: Noah Hsu <i@nn.ci>pull/2771/head
parent
e2bcca2fbd
commit
62a06fa0f9
|
@ -9,7 +9,7 @@ import (
|
||||||
type Driver interface {
|
type Driver interface {
|
||||||
Meta
|
Meta
|
||||||
Reader
|
Reader
|
||||||
Writer
|
//Writer
|
||||||
//Other
|
//Other
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,19 +42,66 @@ type Getter interface {
|
||||||
GetRoot(ctx context.Context) (model.Obj, error)
|
GetRoot(ctx context.Context) (model.Obj, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Writer interface {
|
//type Writer interface {
|
||||||
// MakeDir make a folder named `dirName` in `parentDir`
|
// Mkdir
|
||||||
|
// Move
|
||||||
|
// Rename
|
||||||
|
// Copy
|
||||||
|
// Remove
|
||||||
|
// Put
|
||||||
|
//}
|
||||||
|
|
||||||
|
type Mkdir interface {
|
||||||
MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error
|
MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error
|
||||||
// Move `srcObject` to `dstDir`
|
}
|
||||||
|
|
||||||
|
type Move interface {
|
||||||
Move(ctx context.Context, srcObj, dstDir model.Obj) error
|
Move(ctx context.Context, srcObj, dstDir model.Obj) error
|
||||||
// Rename rename `srcObject` to `newName`
|
}
|
||||||
|
|
||||||
|
type Rename interface {
|
||||||
Rename(ctx context.Context, srcObj model.Obj, newName string) error
|
Rename(ctx context.Context, srcObj model.Obj, newName string) error
|
||||||
// Copy `srcObject` to `dstDir`
|
}
|
||||||
|
|
||||||
|
type Copy interface {
|
||||||
Copy(ctx context.Context, srcObj, dstDir model.Obj) error
|
Copy(ctx context.Context, srcObj, dstDir model.Obj) error
|
||||||
// Remove remove `object`
|
}
|
||||||
|
|
||||||
|
type Remove interface {
|
||||||
Remove(ctx context.Context, obj model.Obj) error
|
Remove(ctx context.Context, obj model.Obj) error
|
||||||
// Put upload `stream` to `parentDir`
|
}
|
||||||
|
|
||||||
|
type Put interface {
|
||||||
Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up UpdateProgress) error
|
Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up UpdateProgress) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//type WriteResult interface {
|
||||||
|
// MkdirResult
|
||||||
|
// MoveResult
|
||||||
|
// RenameResult
|
||||||
|
// CopyResult
|
||||||
|
// PutResult
|
||||||
|
// Remove
|
||||||
|
//}
|
||||||
|
|
||||||
|
type MkdirResult interface {
|
||||||
|
MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MoveResult interface {
|
||||||
|
Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RenameResult interface {
|
||||||
|
Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CopyResult interface {
|
||||||
|
Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PutResult interface {
|
||||||
|
Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up UpdateProgress) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateProgress func(percentage int)
|
type UpdateProgress func(percentage int)
|
||||||
|
|
|
@ -21,7 +21,7 @@ var CopyTaskManager = task.NewTaskManager(3, func(tid *uint64) {
|
||||||
|
|
||||||
// Copy if in the same storage, call move method
|
// Copy if in the same storage, call move method
|
||||||
// if not, add copy task
|
// if not, add copy task
|
||||||
func _copy(ctx context.Context, srcObjPath, dstDirPath string) (bool, error) {
|
func _copy(ctx context.Context, srcObjPath, dstDirPath string, lazyCache ...bool) (bool, error) {
|
||||||
srcStorage, srcObjActualPath, err := op.GetStorageAndActualPath(srcObjPath)
|
srcStorage, srcObjActualPath, err := op.GetStorageAndActualPath(srcObjPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.WithMessage(err, "failed get src storage")
|
return false, errors.WithMessage(err, "failed get src storage")
|
||||||
|
@ -32,7 +32,7 @@ func _copy(ctx context.Context, srcObjPath, dstDirPath string) (bool, error) {
|
||||||
}
|
}
|
||||||
// copy if in the same storage, just call driver.Copy
|
// copy if in the same storage, just call driver.Copy
|
||||||
if srcStorage.GetStorage() == dstStorage.GetStorage() {
|
if srcStorage.GetStorage() == dstStorage.GetStorage() {
|
||||||
return false, op.Copy(ctx, srcStorage, srcObjActualPath, dstDirActualPath)
|
return false, op.Copy(ctx, srcStorage, srcObjActualPath, dstDirActualPath, lazyCache...)
|
||||||
}
|
}
|
||||||
// not in the same storage
|
// not in the same storage
|
||||||
CopyTaskManager.Submit(task.WithCancelCtx(&task.Task[uint64]{
|
CopyTaskManager.Submit(task.WithCancelCtx(&task.Task[uint64]{
|
||||||
|
@ -95,5 +95,5 @@ func copyFileBetween2Storages(tsk *task.Task[uint64], srcStorage, dstStorage dri
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessagef(err, "failed get [%s] stream", srcFilePath)
|
return errors.WithMessagef(err, "failed get [%s] stream", srcFilePath)
|
||||||
}
|
}
|
||||||
return op.Put(tsk.Ctx, dstStorage, dstDirPath, stream, tsk.SetProgress)
|
return op.Put(tsk.Ctx, dstStorage, dstDirPath, stream, tsk.SetProgress, true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,32 +40,32 @@ func Link(ctx context.Context, path string, args model.LinkArgs) (*model.Link, m
|
||||||
return res, file, nil
|
return res, file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeDir(ctx context.Context, path string) error {
|
func MakeDir(ctx context.Context, path string, lazyCache ...bool) error {
|
||||||
err := makeDir(ctx, path)
|
err := makeDir(ctx, path, lazyCache...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed make dir %s: %+v", path, err)
|
log.Errorf("failed make dir %s: %+v", path, err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Move(ctx context.Context, srcPath, dstDirPath string) error {
|
func Move(ctx context.Context, srcPath, dstDirPath string, lazyCache ...bool) error {
|
||||||
err := move(ctx, srcPath, dstDirPath)
|
err := move(ctx, srcPath, dstDirPath, lazyCache...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed move %s to %s: %+v", srcPath, dstDirPath, err)
|
log.Errorf("failed move %s to %s: %+v", srcPath, dstDirPath, err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Copy(ctx context.Context, srcObjPath, dstDirPath string) (bool, error) {
|
func Copy(ctx context.Context, srcObjPath, dstDirPath string, lazyCache ...bool) (bool, error) {
|
||||||
res, err := _copy(ctx, srcObjPath, dstDirPath)
|
res, err := _copy(ctx, srcObjPath, dstDirPath, lazyCache...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed copy %s to %s: %+v", srcObjPath, dstDirPath, err)
|
log.Errorf("failed copy %s to %s: %+v", srcObjPath, dstDirPath, err)
|
||||||
}
|
}
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Rename(ctx context.Context, srcPath, dstName string) error {
|
func Rename(ctx context.Context, srcPath, dstName string, lazyCache ...bool) error {
|
||||||
err := rename(ctx, srcPath, dstName)
|
err := rename(ctx, srcPath, dstName, lazyCache...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed rename %s to %s: %+v", srcPath, dstName, err)
|
log.Errorf("failed rename %s to %s: %+v", srcPath, dstName, err)
|
||||||
}
|
}
|
||||||
|
@ -80,8 +80,8 @@ func Remove(ctx context.Context, path string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func PutDirectly(ctx context.Context, dstDirPath string, file *model.FileStream) error {
|
func PutDirectly(ctx context.Context, dstDirPath string, file *model.FileStream, lazyCache ...bool) error {
|
||||||
err := putDirectly(ctx, dstDirPath, file)
|
err := putDirectly(ctx, dstDirPath, file, lazyCache...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed put %s: %+v", dstDirPath, err)
|
log.Errorf("failed put %s: %+v", dstDirPath, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,15 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeDir(ctx context.Context, path string) error {
|
func makeDir(ctx context.Context, path string, lazyCache ...bool) error {
|
||||||
storage, actualPath, err := op.GetStorageAndActualPath(path)
|
storage, actualPath, err := op.GetStorageAndActualPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessage(err, "failed get storage")
|
return errors.WithMessage(err, "failed get storage")
|
||||||
}
|
}
|
||||||
return op.MakeDir(ctx, storage, actualPath)
|
return op.MakeDir(ctx, storage, actualPath, lazyCache...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func move(ctx context.Context, srcPath, dstDirPath string) error {
|
func move(ctx context.Context, srcPath, dstDirPath string, lazyCache ...bool) error {
|
||||||
srcStorage, srcActualPath, err := op.GetStorageAndActualPath(srcPath)
|
srcStorage, srcActualPath, err := op.GetStorageAndActualPath(srcPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessage(err, "failed get src storage")
|
return errors.WithMessage(err, "failed get src storage")
|
||||||
|
@ -29,15 +29,15 @@ func move(ctx context.Context, srcPath, dstDirPath string) error {
|
||||||
if srcStorage.GetStorage() != dstStorage.GetStorage() {
|
if srcStorage.GetStorage() != dstStorage.GetStorage() {
|
||||||
return errors.WithStack(errs.MoveBetweenTwoStorages)
|
return errors.WithStack(errs.MoveBetweenTwoStorages)
|
||||||
}
|
}
|
||||||
return op.Move(ctx, srcStorage, srcActualPath, dstDirActualPath)
|
return op.Move(ctx, srcStorage, srcActualPath, dstDirActualPath, lazyCache...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rename(ctx context.Context, srcPath, dstName string) error {
|
func rename(ctx context.Context, srcPath, dstName string, lazyCache ...bool) error {
|
||||||
storage, srcActualPath, err := op.GetStorageAndActualPath(srcPath)
|
storage, srcActualPath, err := op.GetStorageAndActualPath(srcPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessage(err, "failed get storage")
|
return errors.WithMessage(err, "failed get storage")
|
||||||
}
|
}
|
||||||
return op.Rename(ctx, storage, srcActualPath, dstName)
|
return op.Rename(ctx, storage, srcActualPath, dstName, lazyCache...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func remove(ctx context.Context, path string) error {
|
func remove(ctx context.Context, path string) error {
|
||||||
|
|
|
@ -36,14 +36,14 @@ func putAsTask(dstDirPath string, file *model.FileStream) error {
|
||||||
UploadTaskManager.Submit(task.WithCancelCtx(&task.Task[uint64]{
|
UploadTaskManager.Submit(task.WithCancelCtx(&task.Task[uint64]{
|
||||||
Name: fmt.Sprintf("upload %s to [%s](%s)", file.GetName(), storage.GetStorage().MountPath, dstDirActualPath),
|
Name: fmt.Sprintf("upload %s to [%s](%s)", file.GetName(), storage.GetStorage().MountPath, dstDirActualPath),
|
||||||
Func: func(task *task.Task[uint64]) error {
|
Func: func(task *task.Task[uint64]) error {
|
||||||
return op.Put(task.Ctx, storage, dstDirActualPath, file, nil)
|
return op.Put(task.Ctx, storage, dstDirActualPath, file, nil, true)
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// putDirect put the file and return after finish
|
// putDirect put the file and return after finish
|
||||||
func putDirectly(ctx context.Context, dstDirPath string, file *model.FileStream) error {
|
func putDirectly(ctx context.Context, dstDirPath string, file *model.FileStream, lazyCache ...bool) error {
|
||||||
storage, dstDirActualPath, err := op.GetStorageAndActualPath(dstDirPath)
|
storage, dstDirActualPath, err := op.GetStorageAndActualPath(dstDirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessage(err, "failed get storage")
|
return errors.WithMessage(err, "failed get storage")
|
||||||
|
@ -51,5 +51,5 @@ func putDirectly(ctx context.Context, dstDirPath string, file *model.FileStream)
|
||||||
if storage.Config().NoUpload {
|
if storage.Config().NoUpload {
|
||||||
return errors.WithStack(errs.UploadNotSupported)
|
return errors.WithStack(errs.UploadNotSupported)
|
||||||
}
|
}
|
||||||
return op.Put(ctx, storage, dstDirActualPath, file, nil)
|
return op.Put(ctx, storage, dstDirActualPath, file, nil, lazyCache...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,12 +99,45 @@ func ExtractFolder(objs []Obj, extractFolder string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap
|
||||||
|
func WrapObjName(objs Obj) Obj {
|
||||||
|
return &ObjWrapName{Obj: objs}
|
||||||
|
}
|
||||||
|
|
||||||
func WrapObjsName(objs []Obj) {
|
func WrapObjsName(objs []Obj) {
|
||||||
for i := 0; i < len(objs); i++ {
|
for i := 0; i < len(objs); i++ {
|
||||||
objs[i] = &ObjWrapName{Obj: objs[i]}
|
objs[i] = &ObjWrapName{Obj: objs[i]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UnwrapObjs(obj Obj) Obj {
|
||||||
|
if unwrap, ok := obj.(UnwrapObj); ok {
|
||||||
|
obj = unwrap.Unwrap()
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetThumb(obj Obj) (thumb string, ok bool) {
|
||||||
|
if obj, ok := obj.(Thumb); ok {
|
||||||
|
return obj.Thumb(), true
|
||||||
|
}
|
||||||
|
if unwrap, ok := obj.(UnwrapObj); ok {
|
||||||
|
return GetThumb(unwrap.Unwrap())
|
||||||
|
}
|
||||||
|
return thumb, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUrl(obj Obj) (url string, ok bool) {
|
||||||
|
if obj, ok := obj.(URL); ok {
|
||||||
|
return obj.URL(), true
|
||||||
|
}
|
||||||
|
if unwrap, ok := obj.(UnwrapObj); ok {
|
||||||
|
return GetUrl(unwrap.Unwrap())
|
||||||
|
}
|
||||||
|
return url, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge
|
||||||
func NewObjMerge() *ObjMerge {
|
func NewObjMerge() *ObjMerge {
|
||||||
return &ObjMerge{
|
return &ObjMerge{
|
||||||
set: mapset.NewSet[string](),
|
set: mapset.NewSet[string](),
|
||||||
|
|
|
@ -21,6 +21,49 @@ import (
|
||||||
var listCache = cache.NewMemCache(cache.WithShards[[]model.Obj](64))
|
var listCache = cache.NewMemCache(cache.WithShards[[]model.Obj](64))
|
||||||
var listG singleflight.Group[[]model.Obj]
|
var listG singleflight.Group[[]model.Obj]
|
||||||
|
|
||||||
|
func updateCacheObj(storage driver.Driver, path string, oldObj model.Obj, newObj model.Obj) {
|
||||||
|
key := Key(storage, path)
|
||||||
|
objs, ok := listCache.Get(key)
|
||||||
|
if ok {
|
||||||
|
for i, obj := range objs {
|
||||||
|
if obj.GetName() == oldObj.GetName() {
|
||||||
|
objs[i] = newObj
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func delCacheObj(storage driver.Driver, path string, obj model.Obj) {
|
||||||
|
key := Key(storage, path)
|
||||||
|
objs, ok := listCache.Get(key)
|
||||||
|
if ok {
|
||||||
|
for i, oldObj := range objs {
|
||||||
|
if oldObj.GetName() == obj.GetName() {
|
||||||
|
objs = append(objs[:i], objs[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addCacheObj(storage driver.Driver, path string, newObj model.Obj) {
|
||||||
|
key := Key(storage, path)
|
||||||
|
objs, ok := listCache.Get(key)
|
||||||
|
if ok {
|
||||||
|
for i, obj := range objs {
|
||||||
|
if obj.GetName() == newObj.GetName() {
|
||||||
|
objs[i] = newObj
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objs = append(objs, newObj)
|
||||||
|
listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ClearCache(storage driver.Driver, path string) {
|
func ClearCache(storage driver.Driver, path string) {
|
||||||
listCache.Del(Key(storage, path))
|
listCache.Del(Key(storage, path))
|
||||||
}
|
}
|
||||||
|
@ -37,7 +80,7 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||||
path = utils.FixAndCleanPath(path)
|
path = utils.FixAndCleanPath(path)
|
||||||
log.Debugf("op.List %s", path)
|
log.Debugf("op.List %s", path)
|
||||||
key := Key(storage, path)
|
key := Key(storage, path)
|
||||||
if len(refresh) == 0 || !refresh[0] {
|
if !utils.IsBool(refresh...) {
|
||||||
if files, ok := listCache.Get(key); ok {
|
if files, ok := listCache.Get(key); ok {
|
||||||
log.Debugf("use cache when list %s", path)
|
log.Debugf("use cache when list %s", path)
|
||||||
return files, nil
|
return files, nil
|
||||||
|
@ -148,10 +191,7 @@ func GetUnwrap(ctx context.Context, storage driver.Driver, path string) (model.O
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if unwrap, ok := obj.(model.UnwrapObj); ok {
|
return model.UnwrapObjs(obj), err
|
||||||
obj = unwrap.Unwrap()
|
|
||||||
}
|
|
||||||
return obj, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var linkCache = cache.NewMemCache(cache.WithShards[*model.Link](16))
|
var linkCache = cache.NewMemCache(cache.WithShards[*model.Link](16))
|
||||||
|
@ -169,7 +209,7 @@ func Link(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
return nil, nil, errors.WithStack(errs.NotFile)
|
return nil, nil, errors.WithStack(errs.NotFile)
|
||||||
}
|
}
|
||||||
key := stdpath.Join(storage.GetStorage().MountPath, path) + ":" + args.IP
|
key := Key(storage, path) + ":" + args.IP
|
||||||
if link, ok := linkCache.Get(key); ok {
|
if link, ok := linkCache.Get(key); ok {
|
||||||
return link, file, nil
|
return link, file, nil
|
||||||
}
|
}
|
||||||
|
@ -206,7 +246,7 @@ func Other(ctx context.Context, storage driver.Driver, args model.FsOtherArgs) (
|
||||||
|
|
||||||
var mkdirG singleflight.Group[interface{}]
|
var mkdirG singleflight.Group[interface{}]
|
||||||
|
|
||||||
func MakeDir(ctx context.Context, storage driver.Driver, path string) error {
|
func MakeDir(ctx context.Context, storage driver.Driver, path string, lazyCache ...bool) error {
|
||||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||||
}
|
}
|
||||||
|
@ -227,32 +267,124 @@ func MakeDir(ctx context.Context, storage driver.Driver, path string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithMessagef(err, "failed to get parent dir [%s]", parentPath)
|
return nil, errors.WithMessagef(err, "failed to get parent dir [%s]", parentPath)
|
||||||
}
|
}
|
||||||
err = storage.MakeDir(ctx, parentDir, dirName)
|
|
||||||
|
switch s := storage.(type) {
|
||||||
|
case driver.MkdirResult:
|
||||||
|
var newObj model.Obj
|
||||||
|
newObj, err = s.MakeDir(ctx, parentDir, dirName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
if newObj != nil {
|
||||||
|
addCacheObj(storage, parentPath, model.WrapObjName(newObj))
|
||||||
|
} else if !utils.IsBool(lazyCache...) {
|
||||||
ClearCache(storage, parentPath)
|
ClearCache(storage, parentPath)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
case driver.Mkdir:
|
||||||
|
err = s.MakeDir(ctx, parentDir, dirName)
|
||||||
|
if err == nil && !utils.IsBool(lazyCache...) {
|
||||||
|
ClearCache(storage, parentPath)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, errs.NotImplement
|
||||||
|
}
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
} else {
|
}
|
||||||
return nil, errors.WithMessage(err, "failed to check if dir exists")
|
return nil, errors.WithMessage(err, "failed to check if dir exists")
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// dir exists
|
// dir exists
|
||||||
if f.IsDir() {
|
if f.IsDir() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
} else {
|
}
|
||||||
// dir to make is a file
|
// dir to make is a file
|
||||||
return nil, errors.New("file exists")
|
return nil, errors.New("file exists")
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Move(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string) error {
|
func Move(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string, lazyCache ...bool) error {
|
||||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||||
}
|
}
|
||||||
|
srcPath = utils.FixAndCleanPath(srcPath)
|
||||||
|
dstDirPath = utils.FixAndCleanPath(dstDirPath)
|
||||||
|
srcRawObj, err := Get(ctx, storage, srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, "failed to get src object")
|
||||||
|
}
|
||||||
|
srcObj := model.UnwrapObjs(srcRawObj)
|
||||||
|
dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, "failed to get dst dir")
|
||||||
|
}
|
||||||
|
srcDirPath := stdpath.Dir(srcPath)
|
||||||
|
|
||||||
|
switch s := storage.(type) {
|
||||||
|
case driver.MoveResult:
|
||||||
|
var newObj model.Obj
|
||||||
|
newObj, err = s.Move(ctx, srcObj, dstDir)
|
||||||
|
if err == nil {
|
||||||
|
delCacheObj(storage, srcDirPath, srcRawObj)
|
||||||
|
if newObj != nil {
|
||||||
|
addCacheObj(storage, dstDirPath, model.WrapObjName(newObj))
|
||||||
|
} else if !utils.IsBool(lazyCache...) {
|
||||||
|
ClearCache(storage, dstDirPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case driver.Move:
|
||||||
|
err = s.Move(ctx, srcObj, dstDir)
|
||||||
|
if err == nil {
|
||||||
|
delCacheObj(storage, srcDirPath, srcRawObj)
|
||||||
|
if !utils.IsBool(lazyCache...) {
|
||||||
|
ClearCache(storage, dstDirPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Rename(ctx context.Context, storage driver.Driver, srcPath, dstName string, lazyCache ...bool) error {
|
||||||
|
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||||
|
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||||
|
}
|
||||||
|
srcPath = utils.FixAndCleanPath(srcPath)
|
||||||
|
srcRawObj, err := Get(ctx, storage, srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, "failed to get src object")
|
||||||
|
}
|
||||||
|
srcObj := model.UnwrapObjs(srcRawObj)
|
||||||
|
srcDirPath := stdpath.Dir(srcPath)
|
||||||
|
|
||||||
|
switch s := storage.(type) {
|
||||||
|
case driver.RenameResult:
|
||||||
|
var newObj model.Obj
|
||||||
|
newObj, err = s.Rename(ctx, srcObj, dstName)
|
||||||
|
if err == nil {
|
||||||
|
if newObj != nil {
|
||||||
|
updateCacheObj(storage, srcDirPath, srcRawObj, model.WrapObjName(newObj))
|
||||||
|
} else if !utils.IsBool(lazyCache...) {
|
||||||
|
ClearCache(storage, srcDirPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case driver.Rename:
|
||||||
|
err = s.Rename(ctx, srcObj, dstName)
|
||||||
|
if err == nil && !utils.IsBool(lazyCache...) {
|
||||||
|
ClearCache(storage, srcDirPath)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy Just copy file[s] in a storage
|
||||||
|
func Copy(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string, lazyCache ...bool) error {
|
||||||
|
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||||
|
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||||
|
}
|
||||||
|
srcPath = utils.FixAndCleanPath(srcPath)
|
||||||
|
dstDirPath = utils.FixAndCleanPath(dstDirPath)
|
||||||
srcObj, err := GetUnwrap(ctx, storage, srcPath)
|
srcObj, err := GetUnwrap(ctx, storage, srcPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessage(err, "failed to get src object")
|
return errors.WithMessage(err, "failed to get src object")
|
||||||
|
@ -261,38 +393,35 @@ func Move(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessage(err, "failed to get dst dir")
|
return errors.WithMessage(err, "failed to get dst dir")
|
||||||
}
|
}
|
||||||
return errors.WithStack(storage.Move(ctx, srcObj, dstDir))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Rename(ctx context.Context, storage driver.Driver, srcPath, dstName string) error {
|
switch s := storage.(type) {
|
||||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
case driver.CopyResult:
|
||||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
var newObj model.Obj
|
||||||
|
newObj, err = s.Copy(ctx, srcObj, dstDir)
|
||||||
|
if err == nil {
|
||||||
|
if newObj != nil {
|
||||||
|
addCacheObj(storage, dstDirPath, model.WrapObjName(newObj))
|
||||||
|
} else if !utils.IsBool(lazyCache...) {
|
||||||
|
ClearCache(storage, dstDirPath)
|
||||||
}
|
}
|
||||||
srcObj, err := GetUnwrap(ctx, storage, srcPath)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithMessage(err, "failed to get src object")
|
|
||||||
}
|
}
|
||||||
return errors.WithStack(storage.Rename(ctx, srcObj, dstName))
|
case driver.Copy:
|
||||||
}
|
err = s.Copy(ctx, srcObj, dstDir)
|
||||||
|
if err == nil && !utils.IsBool(lazyCache...) {
|
||||||
// Copy Just copy file[s] in a storage
|
ClearCache(storage, dstDirPath)
|
||||||
func Copy(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string) error {
|
|
||||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
|
||||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
|
||||||
}
|
}
|
||||||
srcObj, err := GetUnwrap(ctx, storage, srcPath)
|
default:
|
||||||
if err != nil {
|
return errs.NotImplement
|
||||||
return errors.WithMessage(err, "failed to get src object")
|
|
||||||
}
|
}
|
||||||
dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
|
return errors.WithStack(err)
|
||||||
return errors.WithStack(storage.Copy(ctx, srcObj, dstDir))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Remove(ctx context.Context, storage driver.Driver, path string) error {
|
func Remove(ctx context.Context, storage driver.Driver, path string) error {
|
||||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||||
}
|
}
|
||||||
obj, err := GetUnwrap(ctx, storage, path)
|
path = utils.FixAndCleanPath(path)
|
||||||
|
rawObj, err := Get(ctx, storage, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if object not found, it's ok
|
// if object not found, it's ok
|
||||||
if errs.IsObjectNotFound(err) {
|
if errs.IsObjectNotFound(err) {
|
||||||
|
@ -300,31 +429,21 @@ func Remove(ctx context.Context, storage driver.Driver, path string) error {
|
||||||
}
|
}
|
||||||
return errors.WithMessage(err, "failed to get object")
|
return errors.WithMessage(err, "failed to get object")
|
||||||
}
|
}
|
||||||
err = storage.Remove(ctx, obj)
|
dirPath := stdpath.Dir(path)
|
||||||
|
|
||||||
|
switch s := storage.(type) {
|
||||||
|
case driver.Remove:
|
||||||
|
err = s.Remove(ctx, model.UnwrapObjs(rawObj))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
key := Key(storage, stdpath.Dir(path))
|
delCacheObj(storage, dirPath, rawObj)
|
||||||
if objs, ok := listCache.Get(key); ok {
|
|
||||||
j := -1
|
|
||||||
for i, m := range objs {
|
|
||||||
if m.GetName() == obj.GetName() {
|
|
||||||
j = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if j >= 0 && j < len(objs) {
|
|
||||||
objs = append(objs[:j], objs[j+1:]...)
|
|
||||||
listCache.Set(key, objs)
|
|
||||||
} else {
|
|
||||||
log.Debugf("not found obj")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Debugf("not found parent cache")
|
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return errs.NotImplement
|
||||||
}
|
}
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *model.FileStream, up driver.UpdateProgress) error {
|
func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *model.FileStream, up driver.UpdateProgress, lazyCache ...bool) error {
|
||||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||||
}
|
}
|
||||||
|
@ -342,6 +461,7 @@ func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *mo
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
// if file exist and size = 0, delete it
|
// if file exist and size = 0, delete it
|
||||||
|
dstDirPath = utils.FixAndCleanPath(dstDirPath)
|
||||||
dstPath := stdpath.Join(dstDirPath, file.GetName())
|
dstPath := stdpath.Join(dstDirPath, file.GetName())
|
||||||
fi, err := GetUnwrap(ctx, storage, dstPath)
|
fi, err := GetUnwrap(ctx, storage, dstPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -367,7 +487,26 @@ func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *mo
|
||||||
if up == nil {
|
if up == nil {
|
||||||
up = func(p int) {}
|
up = func(p int) {}
|
||||||
}
|
}
|
||||||
err = storage.Put(ctx, parentDir, file, up)
|
|
||||||
|
switch s := storage.(type) {
|
||||||
|
case driver.PutResult:
|
||||||
|
var newObj model.Obj
|
||||||
|
newObj, err = s.Put(ctx, parentDir, file, up)
|
||||||
|
if err == nil {
|
||||||
|
if newObj != nil {
|
||||||
|
addCacheObj(storage, dstDirPath, model.WrapObjName(newObj))
|
||||||
|
} else if !utils.IsBool(lazyCache...) {
|
||||||
|
ClearCache(storage, dstDirPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case driver.Put:
|
||||||
|
err = s.Put(ctx, parentDir, file, up)
|
||||||
|
if err == nil && !utils.IsBool(lazyCache...) {
|
||||||
|
ClearCache(storage, dstDirPath)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
log.Debugf("put file [%s] done", file.GetName())
|
log.Debugf("put file [%s] done", file.GetName())
|
||||||
//if err == nil {
|
//if err == nil {
|
||||||
// //clear cache
|
// //clear cache
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
func IsBool(bs ...bool) bool {
|
||||||
|
return len(bs) > 0 && bs[0]
|
||||||
|
}
|
|
@ -48,7 +48,6 @@ func FsMkdir(c *gin.Context) {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fs.ClearCache(stdpath.Dir(reqPath))
|
|
||||||
common.SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,15 +82,13 @@ func FsMove(c *gin.Context) {
|
||||||
common.ErrorResp(c, err, 403)
|
common.ErrorResp(c, err, 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, name := range req.Names {
|
for i, name := range req.Names {
|
||||||
err := fs.Move(c, stdpath.Join(srcDir, name), dstDir)
|
err := fs.Move(c, stdpath.Join(srcDir, name), dstDir, len(req.Names) > i+1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fs.ClearCache(srcDir)
|
|
||||||
fs.ClearCache(dstDir)
|
|
||||||
common.SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,8 +118,8 @@ func FsCopy(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var addedTask []string
|
var addedTask []string
|
||||||
for _, name := range req.Names {
|
for i, name := range req.Names {
|
||||||
ok, err := fs.Copy(c, stdpath.Join(srcDir, name), dstDir)
|
ok, err := fs.Copy(c, stdpath.Join(srcDir, name), dstDir, len(req.Names) > i+1)
|
||||||
if ok {
|
if ok {
|
||||||
addedTask = append(addedTask, name)
|
addedTask = append(addedTask, name)
|
||||||
}
|
}
|
||||||
|
@ -131,9 +128,6 @@ func FsCopy(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(req.Names) != len(addedTask) {
|
|
||||||
fs.ClearCache(dstDir)
|
|
||||||
}
|
|
||||||
if len(addedTask) > 0 {
|
if len(addedTask) > 0 {
|
||||||
common.SuccessResp(c, fmt.Sprintf("Added %d tasks", len(addedTask)))
|
common.SuccessResp(c, fmt.Sprintf("Added %d tasks", len(addedTask)))
|
||||||
} else {
|
} else {
|
||||||
|
@ -166,7 +160,6 @@ func FsRename(c *gin.Context) {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fs.ClearCache(stdpath.Dir(reqPath))
|
|
||||||
common.SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,10 +191,7 @@ func pagination(objs []model.Obj, req *model.PageReq) (int, []model.Obj) {
|
||||||
func toObjsResp(objs []model.Obj, parent string, encrypt bool) []ObjResp {
|
func toObjsResp(objs []model.Obj, parent string, encrypt bool) []ObjResp {
|
||||||
var resp []ObjResp
|
var resp []ObjResp
|
||||||
for _, obj := range objs {
|
for _, obj := range objs {
|
||||||
thumb := ""
|
thumb, _ := model.GetThumb(obj)
|
||||||
if t, ok := obj.(model.Thumb); ok {
|
|
||||||
thumb = t.Thumb()
|
|
||||||
}
|
|
||||||
resp = append(resp, ObjResp{
|
resp = append(resp, ObjResp{
|
||||||
Name: obj.GetName(),
|
Name: obj.GetName(),
|
||||||
Size: obj.GetSize(),
|
Size: obj.GetSize(),
|
||||||
|
@ -276,8 +273,8 @@ func FsGet(c *gin.Context) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// file have raw url
|
// file have raw url
|
||||||
if u, ok := obj.(model.URL); ok {
|
if url, ok := model.GetUrl(obj); ok {
|
||||||
rawURL = u.URL()
|
rawURL = url
|
||||||
} else {
|
} else {
|
||||||
// if storage is not proxy, use raw url by fs.Link
|
// if storage is not proxy, use raw url by fs.Link
|
||||||
link, _, err := fs.Link(c, reqPath, model.LinkArgs{IP: c.ClientIP(), Header: c.Request.Header})
|
link, _, err := fs.Link(c, reqPath, model.LinkArgs{IP: c.ClientIP(), Header: c.Request.Header})
|
||||||
|
|
|
@ -46,7 +46,7 @@ func FsStream(c *gin.Context) {
|
||||||
if asTask {
|
if asTask {
|
||||||
err = fs.PutAsTask(dir, stream)
|
err = fs.PutAsTask(dir, stream)
|
||||||
} else {
|
} else {
|
||||||
err = fs.PutDirectly(c, dir, stream)
|
err = fs.PutDirectly(c, dir, stream, true)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
|
@ -102,7 +102,7 @@ func FsForm(c *gin.Context) {
|
||||||
if asTask {
|
if asTask {
|
||||||
err = fs.PutAsTask(dir, stream)
|
err = fs.PutAsTask(dir, stream)
|
||||||
} else {
|
} else {
|
||||||
err = fs.PutDirectly(c, dir, stream)
|
err = fs.PutDirectly(c, dir, stream, true)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
|
|
|
@ -46,8 +46,6 @@ func moveFiles(ctx context.Context, src, dst string, overwrite bool) (status int
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
fs.ClearCache(srcDir)
|
|
||||||
fs.ClearCache(dstDir)
|
|
||||||
// TODO if there are no files copy, should return 204
|
// TODO if there are no files copy, should return 204
|
||||||
return http.StatusCreated, nil
|
return http.StatusCreated, nil
|
||||||
}
|
}
|
||||||
|
@ -60,7 +58,6 @@ func copyFiles(ctx context.Context, src, dst string, overwrite bool) (status int
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
fs.ClearCache(path.Dir(dst))
|
|
||||||
// TODO if there are no files copy, should return 204
|
// TODO if there are no files copy, should return 204
|
||||||
return http.StatusCreated, nil
|
return http.StatusCreated, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,7 +337,6 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request) (status int,
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
w.Header().Set("ETag", etag)
|
w.Header().Set("ETag", etag)
|
||||||
fs.ClearCache(path.Dir(reqPath))
|
|
||||||
return http.StatusCreated, nil
|
return http.StatusCreated, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,7 +367,6 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request) (status in
|
||||||
}
|
}
|
||||||
return http.StatusMethodNotAllowed, err
|
return http.StatusMethodNotAllowed, err
|
||||||
}
|
}
|
||||||
fs.ClearCache(path.Dir(reqPath))
|
|
||||||
return http.StatusCreated, nil
|
return http.StatusCreated, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue