diff --git a/models/file.go b/models/file.go index b23842d..098baeb 100644 --- a/models/file.go +++ b/models/file.go @@ -17,6 +17,9 @@ type File struct { FolderID uint `gorm:"index:folder_id"` PolicyID uint Dir string `gorm:"size:65536"` + + // 关联模型 + Policy Policy `gorm:"PRELOAD:false,association_autoupdate:false"` } // Create 创建文件记录 @@ -41,3 +44,12 @@ func (folder *Folder) GetChildFile() ([]File, error) { result := DB.Where("folder_id = ?", folder.ID).Find(&files) return files, result.Error } + +// GetPolicy 获取文件所属策略 +// TODO:test +func (file *File) GetPolicy() *Policy { + if file.Policy.Model.ID == 0 { + file.Policy, _ = GetPolicyByID(file.PolicyID) + } + return &file.Policy +} diff --git a/pkg/filesystem/context.go b/pkg/filesystem/context.go index 3c7406f..adce850 100644 --- a/pkg/filesystem/context.go +++ b/pkg/filesystem/context.go @@ -9,4 +9,6 @@ const ( SavePathCtx // FileHeaderCtx 上传的文件 FileHeaderCtx + // PathCtx 文件或目录的虚拟路径 + PathCtx ) diff --git a/pkg/filesystem/errors.go b/pkg/filesystem/errors.go index c824b50..98aa8c8 100644 --- a/pkg/filesystem/errors.go +++ b/pkg/filesystem/errors.go @@ -1,6 +1,9 @@ package filesystem -import "errors" +import ( + "errors" + "github.com/HFO4/cloudreve/pkg/serializer" +) var ( ErrUnknownPolicyType = errors.New("未知存储策略类型") @@ -8,7 +11,8 @@ var ( ErrFileExtensionNotAllowed = errors.New("不允许上传此类型的文件") ErrInsufficientCapacity = errors.New("容量空间不足") ErrIllegalObjectName = errors.New("目标名称非法") - ErrInsertFileRecord = errors.New("无法插入文件记录") + ErrInsertFileRecord = serializer.NewError(serializer.CodeDBError, "无法插入文件记录", nil) ErrFileExisted = errors.New("同名文件已存在") - ErrPathNotExist = errors.New("路径不存在") + ErrPathNotExist = serializer.NewError(404, "路径不存在", nil) + ErrObjectNotExist = serializer.NewError(404, "文件不存在", nil) ) diff --git a/pkg/filesystem/file.go b/pkg/filesystem/file.go index d9ae304..99943cd 100644 --- a/pkg/filesystem/file.go +++ b/pkg/filesystem/file.go @@ -5,6 +5,7 @@ import ( "errors" model "github.com/HFO4/cloudreve/models" "github.com/HFO4/cloudreve/pkg/serializer" + "github.com/HFO4/cloudreve/pkg/util" "io" ) @@ -36,7 +37,28 @@ func (fs *FileSystem) AddFile(ctx context.Context, parent *model.Folder) (*model return &newFile, nil } -// Download 处理下载文件请求 +// Download 处理下载文件请求,path为虚拟路径 +// TODO:测试 func (fs *FileSystem) Download(ctx context.Context, path string) (io.ReadCloser, error) { + // 触发`下载前`钩子 + err := fs.Trigger(ctx, fs.BeforeFileDownload) + if err != nil { + util.Log().Debug("BeforeFileDownload 钩子执行失败,%s", err) + return nil, err + } + + // 找到文件 + exist, file := fs.IsFileExist(path) + if !exist { + return nil, ErrObjectNotExist + } + + // 将当前存储策略重设为文件使用的 + fs.Policy = file.GetPolicy() + err = fs.dispatchHandler() + if err != nil { + return nil, err + } + return nil, serializer.NewError(serializer.CodeEncryptError, "人都的", errors.New("不是人都的")) } diff --git a/pkg/filesystem/filesystem.go b/pkg/filesystem/filesystem.go index 511598a..bec6f50 100644 --- a/pkg/filesystem/filesystem.go +++ b/pkg/filesystem/filesystem.go @@ -29,10 +29,10 @@ type Handler interface { // FileSystem 管理文件的文件系统 type FileSystem struct { - /* - 文件系统所有者 - */ + // 文件系统所有者 User *model.User + // 操作文件使用的上传策略 + Policy *model.Policy /* 钩子函数 @@ -45,6 +45,8 @@ type FileSystem struct { AfterValidateFailed []Hook // 用户取消上传后 AfterUploadCanceled []Hook + // 文件下载前 + BeforeFileDownload []Hook /* 文件系统处理适配器 @@ -54,21 +56,36 @@ type FileSystem struct { // NewFileSystem 初始化一个文件系统 func NewFileSystem(user *model.User) (*FileSystem, error) { - var handler Handler - - // 根据存储策略类型分配适配器 - switch user.Policy.Type { - case "local": - handler = local.Handler{} - default: - return nil, ErrUnknownPolicyType + fs := &FileSystem{ + User: user, } + // 分配存储策略适配器 + err := fs.dispatchHandler() + // TODO 分配默认钩子 - return &FileSystem{ - User: user, - Handler: handler, - }, nil + return fs, err +} + +// dispatchHandler 根据存储策略分配文件适配器 +// TODO: 测试 +func (fs *FileSystem) dispatchHandler() error { + var policyType string + if fs.Policy == nil { + // 如果没有具体指定,就是用用户当前存储策略 + policyType = fs.User.Policy.Type + } else { + policyType = fs.Policy.Type + } + + // 根据存储策略类型分配适配器 + switch policyType { + case "local": + fs.Handler = local.Handler{} + return nil + default: + return ErrUnknownPolicyType + } } // NewFileSystemFromContext 从gin.Context创建文件系统 diff --git a/pkg/filesystem/hooks.go b/pkg/filesystem/hooks.go index fd89db6..558ac04 100644 --- a/pkg/filesystem/hooks.go +++ b/pkg/filesystem/hooks.go @@ -21,6 +21,8 @@ func (fs *FileSystem) Use(name string, hook Hook) { fs.AfterValidateFailed = append(fs.AfterValidateFailed, hook) case "AfterUploadCanceled": fs.AfterUploadCanceled = append(fs.AfterUploadCanceled, hook) + case "BeforeFileDownload": + fs.BeforeFileDownload = append(fs.BeforeFileDownload, hook) } } @@ -37,6 +39,15 @@ func (fs *FileSystem) Trigger(ctx context.Context, hooks []Hook) error { return nil } +// HookIsFileExist 检查虚拟路径文件是否存在 +func HookIsFileExist(ctx context.Context, fs *FileSystem) error { + filePath := ctx.Value(PathCtx).(string) + if ok, _ := fs.IsFileExist(filePath); ok { + return nil + } + return ErrObjectNotExist +} + // HookValidateFile 一系列对文件检验的集合 func HookValidateFile(ctx context.Context, fs *FileSystem) error { file := ctx.Value(FileHeaderCtx).(FileHeader) @@ -107,10 +118,10 @@ func GenericAfterUpload(ctx context.Context, fs *FileSystem) error { } // 检查文件是否存在 - if fs.IsFileExist(path.Join( + if ok, _ := fs.IsFileExist(path.Join( virtualPath, ctx.Value(FileHeaderCtx).(FileHeader).GetFileName(), - )) { + )); ok { return ErrFileExisted } diff --git a/pkg/filesystem/path.go b/pkg/filesystem/path.go index 0e9cdfb..a06b7fe 100644 --- a/pkg/filesystem/path.go +++ b/pkg/filesystem/path.go @@ -108,7 +108,7 @@ func (fs *FileSystem) CreateDirectory(ctx context.Context, fullPath string) erro } // 是否有同名文件 - if fs.IsFileExist(path.Join(base, dir)) { + if ok, _ := fs.IsFileExist(path.Join(base, dir)); ok { return ErrFileExisted } @@ -133,11 +133,11 @@ func (fs *FileSystem) IsPathExist(path string) (bool, *model.Folder) { } // IsFileExist 返回给定路径的文件是否存在 -func (fs *FileSystem) IsFileExist(fullPath string) bool { +func (fs *FileSystem) IsFileExist(fullPath string) (bool, model.File) { basePath := path.Dir(fullPath) fileName := path.Base(fullPath) - _, err := model.GetFileByPathAndName(basePath, fileName, fs.User.ID) + file, err := model.GetFileByPathAndName(basePath, fileName, fs.User.ID) - return err == nil + return err == nil, file } diff --git a/pkg/filesystem/path_test.go b/pkg/filesystem/path_test.go index 58aa521..718de10 100644 --- a/pkg/filesystem/path_test.go +++ b/pkg/filesystem/path_test.go @@ -21,7 +21,7 @@ func TestFileSystem_IsFileExist(t *testing.T) { mock.ExpectQuery("SELECT(.+)").WithArgs(uint(1), "/s", "1.txt").WillReturnRows( sqlmock.NewRows([]string{"Name"}).AddRow("s"), ) - testResult := fs.IsFileExist("/s/1.txt") + testResult, _ := fs.IsFileExist("/s/1.txt") asserts.True(testResult) asserts.NoError(mock.ExpectationsWereMet()) @@ -29,7 +29,7 @@ func TestFileSystem_IsFileExist(t *testing.T) { mock.ExpectQuery("SELECT(.+)").WithArgs(uint(1), "/ss/dfsd", "1.txt").WillReturnRows( sqlmock.NewRows([]string{"Name"}), ) - testResult = fs.IsFileExist("/ss/dfsd/1.txt") + testResult, _ = fs.IsFileExist("/ss/dfsd/1.txt") asserts.False(testResult) asserts.NoError(mock.ExpectationsWereMet()) } diff --git a/pkg/filesystem/upload.go b/pkg/filesystem/upload.go index 2887ea3..d658527 100644 --- a/pkg/filesystem/upload.go +++ b/pkg/filesystem/upload.go @@ -44,7 +44,7 @@ func (fs *FileSystem) Upload(ctx context.Context, file FileHeader) (err error) { followUpErr := fs.Trigger(ctx, fs.AfterValidateFailed) // 失败后再失败... if followUpErr != nil { - util.Log().Warning("AfterValidateFailed 钩子执行失败,%s", followUpErr) + util.Log().Debug("AfterValidateFailed 钩子执行失败,%s", followUpErr) } return err @@ -85,7 +85,7 @@ func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileHe ctx = context.WithValue(ctx, SavePathCtx, path) err := fs.Trigger(ctx, fs.AfterUploadCanceled) if err != nil { - util.Log().Warning("执行 AfterUploadCanceled 钩子出错,%s", err) + util.Log().Debug("执行 AfterUploadCanceled 钩子出错,%s", err) } } } diff --git a/service/explorer/file.go b/service/explorer/file.go index 1d1b0c5..00079c8 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -18,9 +18,10 @@ func (service *FileDownloadService) Download(ctx context.Context, c *gin.Context fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) - } + // 开始处理下载 + ctx = context.WithValue(ctx, filesystem.GinCtx, c) _, err = fs.Download(ctx, service.Path) if err != nil {