From 438ce024208553a627ed7580754afeb7fec8c73d Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Wed, 20 Nov 2019 15:53:00 +0800 Subject: [PATCH] Modify: re-organize structure of filesystem --- models/file.go | 6 +- pkg/filesystem/file.go | 34 +++++++++ pkg/filesystem/filesystem.go | 143 +---------------------------------- pkg/filesystem/hooks.go | 5 +- pkg/filesystem/image.go | 19 +++++ pkg/filesystem/path.go | 28 +++++++ pkg/filesystem/upload.go | 93 +++++++++++++++++++++++ pkg/filesystem/validator.go | 5 ++ 8 files changed, 191 insertions(+), 142 deletions(-) create mode 100644 pkg/filesystem/file.go create mode 100644 pkg/filesystem/image.go create mode 100644 pkg/filesystem/path.go create mode 100644 pkg/filesystem/upload.go diff --git a/models/file.go b/models/file.go index 05ce596..2645662 100644 --- a/models/file.go +++ b/models/file.go @@ -1,6 +1,9 @@ package model -import "github.com/jinzhu/gorm" +import ( + "github.com/HFO4/cloudreve/pkg/util" + "github.com/jinzhu/gorm" +) // File 文件 type File struct { @@ -19,6 +22,7 @@ type File struct { // Create 创建文件记录 func (file *File) Create() (uint, error) { if err := DB.Create(file).Error; err != nil { + util.Log().Warning("无法插入文件记录, %s", err) return 0, err } return file.ID, nil diff --git a/pkg/filesystem/file.go b/pkg/filesystem/file.go new file mode 100644 index 0000000..7f43778 --- /dev/null +++ b/pkg/filesystem/file.go @@ -0,0 +1,34 @@ +package filesystem + +import ( + "context" + model "github.com/HFO4/cloudreve/models" +) + +/* ============ + 文件相关 + ============ +*/ + +// AddFile 新增文件记录 +func (fs *FileSystem) AddFile(ctx context.Context, parent *model.Folder) (*model.File, error) { + file := ctx.Value(FileHeaderCtx).(FileHeader) + filePath := ctx.Value(SavePathCtx).(string) + + newFile := model.File{ + Name: file.GetFileName(), + SourceName: filePath, + UserID: fs.User.ID, + Size: file.GetSize(), + FolderID: parent.ID, + PolicyID: fs.User.Policy.ID, + Dir: parent.PositionAbsolute, + } + _, err := newFile.Create() + + if err != nil { + return nil, err + } + + return &newFile, nil +} diff --git a/pkg/filesystem/filesystem.go b/pkg/filesystem/filesystem.go index 6049adb..1abe449 100644 --- a/pkg/filesystem/filesystem.go +++ b/pkg/filesystem/filesystem.go @@ -4,11 +4,7 @@ import ( "context" "github.com/HFO4/cloudreve/models" "github.com/HFO4/cloudreve/pkg/filesystem/local" - "github.com/HFO4/cloudreve/pkg/util" - "github.com/gin-gonic/gin" "io" - "path" - "path/filepath" ) // FileHeader 上传来的文件数据处理器 @@ -32,12 +28,12 @@ type Handler interface { // FileSystem 管理文件的文件系统 type FileSystem struct { /* - 文件系统所有者 + 文件系统所有者 */ User *model.User /* - 钩子函数 + 钩子函数 */ // 上传文件前 BeforeUpload func(ctx context.Context, fs *FileSystem) error @@ -49,7 +45,7 @@ type FileSystem struct { AfterUploadCanceled func(ctx context.Context, fs *FileSystem) error /* - 文件系统处理适配器 + 文件系统处理适配器 */ Handler Handler } @@ -72,136 +68,3 @@ func NewFileSystem(user *model.User) (*FileSystem, error) { Handler: handler, }, nil } - -/* ============ - 文件相关 - ============ -*/ - -// AddFile 新增文件记录 -func (fs *FileSystem) AddFile(ctx context.Context, parent *model.Folder) (*model.File, error) { - file := ctx.Value(FileHeaderCtx).(FileHeader) - filePath := ctx.Value(SavePathCtx).(string) - - newFile := model.File{ - Name: file.GetFileName(), - SourceName: filePath, - UserID: fs.User.ID, - Size: file.GetSize(), - FolderID: parent.ID, - PolicyID: fs.User.Policy.ID, - Dir: parent.PositionAbsolute, - } - _, err := newFile.Create() - - if err != nil { - return nil, err - } - - return &newFile, nil -} - -/* ================ - 上传处理相关 - ================ -*/ - -// Upload 上传文件 -func (fs *FileSystem) Upload(ctx context.Context, file FileHeader) (err error) { - ctx = context.WithValue(ctx, FileHeaderCtx, file) - - // 上传前的钩子 - if fs.BeforeUpload != nil { - err = fs.BeforeUpload(ctx, fs) - if err != nil { - return err - } - } - - // 生成文件名和路径 - savePath := fs.GenerateSavePath(ctx, file) - - // 处理客户端未完成上传时,关闭连接 - go fs.CancelUpload(ctx, savePath, file) - - // 保存文件 - err = fs.Handler.Put(ctx, file, savePath) - if err != nil { - return err - } - - // 上传完成后的钩子 - if fs.AfterUpload != nil { - ctx = context.WithValue(ctx, SavePathCtx, savePath) - err = fs.AfterUpload(ctx, fs) - - if err != nil { - // 上传完成后续处理失败 - if fs.AfterValidateFailed != nil { - followUpErr := fs.AfterValidateFailed(ctx, fs) - // 失败后再失败... - if followUpErr != nil { - util.Log().Warning("AfterValidateFailed 钩子执行失败,%s", followUpErr) - } - } - return err - } - } - - return nil -} - -// GenerateSavePath 生成要存放文件的路径 -func (fs *FileSystem) GenerateSavePath(ctx context.Context, file FileHeader) string { - return filepath.Join( - fs.User.Policy.GeneratePath( - fs.User.Model.ID, - file.GetVirtualPath(), - ), - fs.User.Policy.GenerateFileName( - fs.User.Model.ID, - file.GetFileName(), - ), - ) -} - -// CancelUpload 监测客户端取消上传 -func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileHeader) { - ginCtx := ctx.Value(GinCtx).(*gin.Context) - select { - case <-ctx.Done(): - // 客户端正常关闭,不执行操作 - case <-ginCtx.Request.Context().Done(): - // 客户端取消了上传 - if fs.AfterUploadCanceled == nil { - return - } - ctx = context.WithValue(ctx, SavePathCtx, path) - err := fs.AfterUploadCanceled(ctx, fs) - if err != nil { - util.Log().Warning("执行 AfterUploadCanceled 钩子出错,%s", err) - } - } -} - -/* ================= - 路径/目录相关 - ================= -*/ - -// IsPathExist 返回给定目录是否存在 -// 如果存在就返回目录 -func (fs *FileSystem) IsPathExist(path string) (bool, model.Folder) { - folder, err := model.GetFolderByPath(path, fs.User.ID) - return err == nil, folder -} - -// IsFileExist 返回给定路径的文件是否存在 -func (fs *FileSystem) IsFileExist(fullPath string) bool { - basePath := path.Dir(fullPath) - fileName := path.Base(fullPath) - - _, err := model.GetFileByPathAndName(basePath, fileName, fs.User.ID) - - return err == nil -} diff --git a/pkg/filesystem/hooks.go b/pkg/filesystem/hooks.go index 105da3c..194b16b 100644 --- a/pkg/filesystem/hooks.go +++ b/pkg/filesystem/hooks.go @@ -73,12 +73,15 @@ func GenericAfterUpload(ctx context.Context, fs *FileSystem) error { } // 向数据库中插入记录 - _, err := fs.AddFile(ctx, &folder) + file, err := fs.AddFile(ctx, &folder) if err != nil { return errors.New("无法插入文件记录") } + // TODO 是否需要立即获取图像大小? + // 异步尝试生成缩略图 + go fs.GenerateThumbnail(ctx, file) return nil } diff --git a/pkg/filesystem/image.go b/pkg/filesystem/image.go new file mode 100644 index 0000000..412784f --- /dev/null +++ b/pkg/filesystem/image.go @@ -0,0 +1,19 @@ +package filesystem + +import ( + "context" + model "github.com/HFO4/cloudreve/models" +) + +/* =============== + 图像处理相关 + =============== +*/ + +// HandledExtension 可以生成缩略图的文件扩展名 +var HandledExtension = []string{} + +// GenerateThumbnail 尝试为文件生成缩略图 +func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) { + // TODO +} diff --git a/pkg/filesystem/path.go b/pkg/filesystem/path.go new file mode 100644 index 0000000..deb2f65 --- /dev/null +++ b/pkg/filesystem/path.go @@ -0,0 +1,28 @@ +package filesystem + +import ( + model "github.com/HFO4/cloudreve/models" + "path" +) + +/* ================= + 路径/目录相关 + ================= +*/ + +// IsPathExist 返回给定目录是否存在 +// 如果存在就返回目录 +func (fs *FileSystem) IsPathExist(path string) (bool, model.Folder) { + folder, err := model.GetFolderByPath(path, fs.User.ID) + return err == nil, folder +} + +// IsFileExist 返回给定路径的文件是否存在 +func (fs *FileSystem) IsFileExist(fullPath string) bool { + basePath := path.Dir(fullPath) + fileName := path.Base(fullPath) + + _, err := model.GetFileByPathAndName(basePath, fileName, fs.User.ID) + + return err == nil +} diff --git a/pkg/filesystem/upload.go b/pkg/filesystem/upload.go new file mode 100644 index 0000000..134ad4e --- /dev/null +++ b/pkg/filesystem/upload.go @@ -0,0 +1,93 @@ +package filesystem + +import ( + "context" + "github.com/HFO4/cloudreve/pkg/util" + "github.com/gin-gonic/gin" + "path/filepath" +) + +/* ================ + 上传处理相关 + ================ +*/ + +// Upload 上传文件 +func (fs *FileSystem) Upload(ctx context.Context, file FileHeader) (err error) { + ctx = context.WithValue(ctx, FileHeaderCtx, file) + + // 上传前的钩子 + if fs.BeforeUpload != nil { + err = fs.BeforeUpload(ctx, fs) + if err != nil { + return err + } + } + + // 生成文件名和路径 + savePath := fs.GenerateSavePath(ctx, file) + + // 处理客户端未完成上传时,关闭连接 + go fs.CancelUpload(ctx, savePath, file) + + // 保存文件 + err = fs.Handler.Put(ctx, file, savePath) + if err != nil { + return err + } + + // 上传完成后的钩子 + if fs.AfterUpload != nil { + ctx = context.WithValue(ctx, SavePathCtx, savePath) + err = fs.AfterUpload(ctx, fs) + + if err != nil { + // 上传完成后续处理失败 + if fs.AfterValidateFailed != nil { + followUpErr := fs.AfterValidateFailed(ctx, fs) + // 失败后再失败... + if followUpErr != nil { + util.Log().Warning("AfterValidateFailed 钩子执行失败,%s", followUpErr) + } + } + return err + } + } + + util.Log().Info("新文件上传:%s , 大小:%d, 上传者:%s", file.GetFileName(), file.GetSize(), fs.User.Nick) + + return nil +} + +// GenerateSavePath 生成要存放文件的路径 +func (fs *FileSystem) GenerateSavePath(ctx context.Context, file FileHeader) string { + return filepath.Join( + fs.User.Policy.GeneratePath( + fs.User.Model.ID, + file.GetVirtualPath(), + ), + fs.User.Policy.GenerateFileName( + fs.User.Model.ID, + file.GetFileName(), + ), + ) +} + +// CancelUpload 监测客户端取消上传 +func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileHeader) { + ginCtx := ctx.Value(GinCtx).(*gin.Context) + select { + case <-ctx.Done(): + // 客户端正常关闭,不执行操作 + case <-ginCtx.Request.Context().Done(): + // 客户端取消了上传 + if fs.AfterUploadCanceled == nil { + return + } + ctx = context.WithValue(ctx, SavePathCtx, path) + err := fs.AfterUploadCanceled(ctx, fs) + if err != nil { + util.Log().Warning("执行 AfterUploadCanceled 钩子出错,%s", err) + } + } +} diff --git a/pkg/filesystem/validator.go b/pkg/filesystem/validator.go index f749f6c..5ff229c 100644 --- a/pkg/filesystem/validator.go +++ b/pkg/filesystem/validator.go @@ -7,6 +7,11 @@ import ( "strings" ) +/* ========== + 验证器 + ========== +*/ + // 文件/路径名保留字符 var reservedCharacter = []string{"\\", "?", "*", "<", "\"", ":", ">", "/"}