From b6102c3ae5dc18a082476b0ce855f2a264539651 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Thu, 2 Jan 2020 13:10:04 +0800 Subject: [PATCH] Fix: policy should be re-dispatch while getting thumbnails --- models/setting.go | 1 - models/setting_test.go | 18 ++++++++++++++++++ pkg/filesystem/file.go | 15 +++++++++++++++ pkg/filesystem/filesystem.go | 2 ++ pkg/filesystem/image.go | 7 +++---- pkg/filesystem/image_test.go | 26 ++++++++++++++++++-------- pkg/filesystem/remote/handler.go | 15 ++++++++++++++- routers/controllers/slave.go | 17 +++++++++++++++++ routers/router.go | 2 ++ service/explorer/file.go | 22 ++++++++++++++++++++++ 10 files changed, 111 insertions(+), 14 deletions(-) diff --git a/models/setting.go b/models/setting.go index 265ab08..0ed3648 100644 --- a/models/setting.go +++ b/models/setting.go @@ -75,7 +75,6 @@ func GetSiteURL() *url.URL { } // GetIntSetting 获取整形设置值,如果转换失败则返回默认值defaultVal -// TODO 测试 func GetIntSetting(key string, defaultVal int) int { res, err := strconv.Atoi(GetSettingByName(key)) if err != nil { diff --git a/models/setting_test.go b/models/setting_test.go index d9b1b9b..8ccbaf5 100644 --- a/models/setting_test.go +++ b/models/setting_test.go @@ -166,3 +166,21 @@ func TestGetSiteURL(t *testing.T) { asserts.Equal("https://cloudreve.org", siteURL.String()) } } + +func TestGetIntSetting(t *testing.T) { + asserts := assert.New(t) + + // 正常 + { + cache.Set("setting_TestGetIntSetting", "10", 0) + res := GetIntSetting("TestGetIntSetting", 20) + asserts.Equal(10, res) + } + + // 使用默认值 + { + res := GetIntSetting("TestGetIntSetting_2", 20) + asserts.Equal(20, res) + } + +} diff --git a/pkg/filesystem/file.go b/pkg/filesystem/file.go index edf28ec..72e8cfb 100644 --- a/pkg/filesystem/file.go +++ b/pkg/filesystem/file.go @@ -287,6 +287,21 @@ func (fs *FileSystem) resetFileIfNotExist(ctx context.Context, path string) erro return fs.resetPolicyToFirstFile(ctx) } +// resetFileIfNotExist 重设当前目标文件为 id,如果当前目标为空 +func (fs *FileSystem) resetFileIDIfNotExist(ctx context.Context, id uint) error { + // 找到文件 + if len(fs.FileTarget) == 0 { + file, err := model.GetFilesByIDs([]uint{id}, fs.User.ID) + if err != nil || len(file) == 0 { + return ErrObjectNotExist + } + fs.FileTarget = []model.File{file[0]} + } + + // 将当前存储策略重设为文件使用的 + return fs.resetPolicyToFirstFile(ctx) +} + // resetPolicyToFirstFile 将当前存储策略重设为第一个目标文件文件使用的 func (fs *FileSystem) resetPolicyToFirstFile(ctx context.Context) error { if len(fs.FileTarget) == 0 { diff --git a/pkg/filesystem/filesystem.go b/pkg/filesystem/filesystem.go index daa53eb..085ad4e 100644 --- a/pkg/filesystem/filesystem.go +++ b/pkg/filesystem/filesystem.go @@ -146,6 +146,8 @@ func (fs *FileSystem) dispatchHandler() error { // 根据存储策略类型分配适配器 switch policyType { + case "mock": + return nil case "local": fs.Handler = local.Handler{ Policy: currentPolicy, diff --git a/pkg/filesystem/image.go b/pkg/filesystem/image.go index 7c28943..1a4fbec 100644 --- a/pkg/filesystem/image.go +++ b/pkg/filesystem/image.go @@ -21,15 +21,14 @@ var HandledExtension = []string{"jpg", "jpeg", "png", "gif"} // GetThumb 获取文件的缩略图 func (fs *FileSystem) GetThumb(ctx context.Context, id uint) (*response.ContentResponse, error) { // 根据 ID 查找文件 - file, err := model.GetFilesByIDs([]uint{id}, fs.User.ID) - if err != nil || len(file) == 0 || file[0].PicInfo == "" { + err := fs.resetFileIDIfNotExist(ctx, id) + if err != nil || fs.FileTarget[0].PicInfo == "" { return &response.ContentResponse{ Redirect: false, }, ErrObjectNotExist } - fs.FileTarget = []model.File{file[0]} - res, err := fs.Handler.Thumb(ctx, file[0].SourceName) + res, err := fs.Handler.Thumb(ctx, fs.FileTarget[0].SourceName) // TODO 出错时重新生成缩略图 return res, err diff --git a/pkg/filesystem/image_test.go b/pkg/filesystem/image_test.go index 059647b..0381530 100644 --- a/pkg/filesystem/image_test.go +++ b/pkg/filesystem/image_test.go @@ -31,15 +31,15 @@ func CreateTestImage() *os.File { func TestFileSystem_GetThumb(t *testing.T) { asserts := assert.New(t) - fs := FileSystem{ - User: &model.User{ - Model: gorm.Model{ID: 1}, - }, - } ctx := context.Background() // 正常 { + fs := FileSystem{ + User: &model.User{ + Model: gorm.Model{ID: 1}, + }, + } testHandler := new(FileHeaderMock) testHandler.On("Thumb", testMock.Anything, "123.jpg").Return(&response.ContentResponse{URL: "123"}, nil) fs.Handler = testHandler @@ -47,8 +47,14 @@ func TestFileSystem_GetThumb(t *testing.T) { WithArgs(10, 1). WillReturnRows( sqlmock.NewRows( - []string{"id", "pic_info", "source_name"}). - AddRow(10, "10,10", "123.jpg"), + []string{"id", "pic_info", "source_name", "policy_id"}). + AddRow(10, "10,10", "123.jpg", 154), + ) + mock.ExpectQuery("SELECT(.+)"). + WillReturnRows( + sqlmock.NewRows( + []string{"id", "type"}). + AddRow(154, "mock"), ) res, err := fs.GetThumb(ctx, 10) @@ -60,7 +66,11 @@ func TestFileSystem_GetThumb(t *testing.T) { // 文件不存在 { - + fs := FileSystem{ + User: &model.User{ + Model: gorm.Model{ID: 1}, + }, + } mock.ExpectQuery("SELECT(.+)"). WithArgs(10, 1). WillReturnRows( diff --git a/pkg/filesystem/remote/handler.go b/pkg/filesystem/remote/handler.go index e21fd04..353655e 100644 --- a/pkg/filesystem/remote/handler.go +++ b/pkg/filesystem/remote/handler.go @@ -38,6 +38,8 @@ func (handler Handler) getAPI(scope string) string { switch scope { case "delete": controller, _ = url.Parse("/api/v3/slave/delete") + case "thumb": + controller, _ = url.Parse("/api/v3/slave/thumb") } return serverURL.ResolveReference(controller).String() @@ -101,7 +103,18 @@ func (handler Handler) Delete(ctx context.Context, files []string) ([]string, er // Thumb 获取文件缩略图 func (handler Handler) Thumb(ctx context.Context, path string) (*response.ContentResponse, error) { - return nil, errors.New("未实现") + sourcePath := base64.RawURLEncoding.EncodeToString([]byte(path)) + thumbURL := handler.getAPI("thumb") + "/" + sourcePath + ttl := model.GetIntSetting("slave_api_timeout", 60) + signedThumbURL, err := auth.SignURI(handler.AuthInstance, thumbURL, int64(ttl)) + if err != nil { + return nil, err + } + + return &response.ContentResponse{ + Redirect: true, + URL: signedThumbURL.String(), + }, nil } // Source 获取外链URL diff --git a/routers/controllers/slave.go b/routers/controllers/slave.go index f6f39bc..5f2490b 100644 --- a/routers/controllers/slave.go +++ b/routers/controllers/slave.go @@ -115,6 +115,23 @@ func SlavePreview(c *gin.Context) { } } +// SlaveThumb 从机文件缩略图 +func SlaveThumb(c *gin.Context) { + // 创建上下文 + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + var service explorer.SlaveFileService + if err := c.ShouldBindUri(&service); err == nil { + res := service.Thumb(ctx, c) + if res.Code != 0 { + c.JSON(200, res) + } + } else { + c.JSON(200, ErrorResponse(err)) + } +} + // SlaveDelete 从机删除 func SlaveDelete(c *gin.Context) { // 创建上下文 diff --git a/routers/router.go b/routers/router.go index c371a6a..cac6f7d 100644 --- a/routers/router.go +++ b/routers/router.go @@ -39,6 +39,8 @@ func InitSlaveRouter() *gin.Engine { v3.GET("download/:speed/:path/:name", controllers.SlaveDownload) // 预览 / 外链 v3.GET("source/:speed/:path/:name", controllers.SlavePreview) + // 缩略图 + v3.GET("thumb/:path", controllers.SlaveThumb) // 删除文件 v3.POST("delete", controllers.SlaveDelete) } diff --git a/service/explorer/file.go b/service/explorer/file.go index 7b2e28e..1e1215e 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -43,6 +43,11 @@ type SlaveDownloadService struct { Speed int `uri:"speed" binding:"min=0"` } +// SlaveFileService 从机单文件文件相关服务 +type SlaveFileService struct { + PathEncoded string `uri:"path" binding:"required"` +} + // SlaveFilesService 从机多文件相关服务 type SlaveFilesService struct { Files []string `json:"files" binding:"required,gt=0"` @@ -368,3 +373,20 @@ func (service *SlaveFilesService) Delete(ctx context.Context, c *gin.Context) se } return serializer.Response{Code: 0} } + +// Thumb 通过签名URL获取从机文件缩略图 +func (service *SlaveFileService) Thumb(ctx context.Context, c *gin.Context) serializer.Response { + // 创建文件系统 + fs, err := filesystem.NewAnonymousFileSystem() + if err != nil { + return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + } + defer fs.Recycle() + + // 解码文件路径 + fileSource, err := base64.RawURLEncoding.DecodeString(service.PathEncoded) + if err != nil { + return serializer.ParamErr("无法解析的文件地址", err) + } + +}