mirror of https://github.com/cloudreve/Cloudreve
Fix: policy should be re-dispatch while getting thumbnails
parent
a75be3a927
commit
b6102c3ae5
|
@ -75,7 +75,6 @@ func GetSiteURL() *url.URL {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIntSetting 获取整形设置值,如果转换失败则返回默认值defaultVal
|
// GetIntSetting 获取整形设置值,如果转换失败则返回默认值defaultVal
|
||||||
// TODO 测试
|
|
||||||
func GetIntSetting(key string, defaultVal int) int {
|
func GetIntSetting(key string, defaultVal int) int {
|
||||||
res, err := strconv.Atoi(GetSettingByName(key))
|
res, err := strconv.Atoi(GetSettingByName(key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -166,3 +166,21 @@ func TestGetSiteURL(t *testing.T) {
|
||||||
asserts.Equal("https://cloudreve.org", siteURL.String())
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -287,6 +287,21 @@ func (fs *FileSystem) resetFileIfNotExist(ctx context.Context, path string) erro
|
||||||
return fs.resetPolicyToFirstFile(ctx)
|
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 将当前存储策略重设为第一个目标文件文件使用的
|
// resetPolicyToFirstFile 将当前存储策略重设为第一个目标文件文件使用的
|
||||||
func (fs *FileSystem) resetPolicyToFirstFile(ctx context.Context) error {
|
func (fs *FileSystem) resetPolicyToFirstFile(ctx context.Context) error {
|
||||||
if len(fs.FileTarget) == 0 {
|
if len(fs.FileTarget) == 0 {
|
||||||
|
|
|
@ -146,6 +146,8 @@ func (fs *FileSystem) dispatchHandler() error {
|
||||||
|
|
||||||
// 根据存储策略类型分配适配器
|
// 根据存储策略类型分配适配器
|
||||||
switch policyType {
|
switch policyType {
|
||||||
|
case "mock":
|
||||||
|
return nil
|
||||||
case "local":
|
case "local":
|
||||||
fs.Handler = local.Handler{
|
fs.Handler = local.Handler{
|
||||||
Policy: currentPolicy,
|
Policy: currentPolicy,
|
||||||
|
|
|
@ -21,15 +21,14 @@ var HandledExtension = []string{"jpg", "jpeg", "png", "gif"}
|
||||||
// GetThumb 获取文件的缩略图
|
// GetThumb 获取文件的缩略图
|
||||||
func (fs *FileSystem) GetThumb(ctx context.Context, id uint) (*response.ContentResponse, error) {
|
func (fs *FileSystem) GetThumb(ctx context.Context, id uint) (*response.ContentResponse, error) {
|
||||||
// 根据 ID 查找文件
|
// 根据 ID 查找文件
|
||||||
file, err := model.GetFilesByIDs([]uint{id}, fs.User.ID)
|
err := fs.resetFileIDIfNotExist(ctx, id)
|
||||||
if err != nil || len(file) == 0 || file[0].PicInfo == "" {
|
if err != nil || fs.FileTarget[0].PicInfo == "" {
|
||||||
return &response.ContentResponse{
|
return &response.ContentResponse{
|
||||||
Redirect: false,
|
Redirect: false,
|
||||||
}, ErrObjectNotExist
|
}, ErrObjectNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.FileTarget = []model.File{file[0]}
|
res, err := fs.Handler.Thumb(ctx, fs.FileTarget[0].SourceName)
|
||||||
res, err := fs.Handler.Thumb(ctx, file[0].SourceName)
|
|
||||||
|
|
||||||
// TODO 出错时重新生成缩略图
|
// TODO 出错时重新生成缩略图
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -31,15 +31,15 @@ func CreateTestImage() *os.File {
|
||||||
|
|
||||||
func TestFileSystem_GetThumb(t *testing.T) {
|
func TestFileSystem_GetThumb(t *testing.T) {
|
||||||
asserts := assert.New(t)
|
asserts := assert.New(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 正常
|
||||||
|
{
|
||||||
fs := FileSystem{
|
fs := FileSystem{
|
||||||
User: &model.User{
|
User: &model.User{
|
||||||
Model: gorm.Model{ID: 1},
|
Model: gorm.Model{ID: 1},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
// 正常
|
|
||||||
{
|
|
||||||
testHandler := new(FileHeaderMock)
|
testHandler := new(FileHeaderMock)
|
||||||
testHandler.On("Thumb", testMock.Anything, "123.jpg").Return(&response.ContentResponse{URL: "123"}, nil)
|
testHandler.On("Thumb", testMock.Anything, "123.jpg").Return(&response.ContentResponse{URL: "123"}, nil)
|
||||||
fs.Handler = testHandler
|
fs.Handler = testHandler
|
||||||
|
@ -47,8 +47,14 @@ func TestFileSystem_GetThumb(t *testing.T) {
|
||||||
WithArgs(10, 1).
|
WithArgs(10, 1).
|
||||||
WillReturnRows(
|
WillReturnRows(
|
||||||
sqlmock.NewRows(
|
sqlmock.NewRows(
|
||||||
[]string{"id", "pic_info", "source_name"}).
|
[]string{"id", "pic_info", "source_name", "policy_id"}).
|
||||||
AddRow(10, "10,10", "123.jpg"),
|
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)
|
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(.+)").
|
mock.ExpectQuery("SELECT(.+)").
|
||||||
WithArgs(10, 1).
|
WithArgs(10, 1).
|
||||||
WillReturnRows(
|
WillReturnRows(
|
||||||
|
|
|
@ -38,6 +38,8 @@ func (handler Handler) getAPI(scope string) string {
|
||||||
switch scope {
|
switch scope {
|
||||||
case "delete":
|
case "delete":
|
||||||
controller, _ = url.Parse("/api/v3/slave/delete")
|
controller, _ = url.Parse("/api/v3/slave/delete")
|
||||||
|
case "thumb":
|
||||||
|
controller, _ = url.Parse("/api/v3/slave/thumb")
|
||||||
}
|
}
|
||||||
|
|
||||||
return serverURL.ResolveReference(controller).String()
|
return serverURL.ResolveReference(controller).String()
|
||||||
|
@ -101,7 +103,18 @@ func (handler Handler) Delete(ctx context.Context, files []string) ([]string, er
|
||||||
|
|
||||||
// Thumb 获取文件缩略图
|
// Thumb 获取文件缩略图
|
||||||
func (handler Handler) Thumb(ctx context.Context, path string) (*response.ContentResponse, error) {
|
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
|
// Source 获取外链URL
|
||||||
|
|
|
@ -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 从机删除
|
// SlaveDelete 从机删除
|
||||||
func SlaveDelete(c *gin.Context) {
|
func SlaveDelete(c *gin.Context) {
|
||||||
// 创建上下文
|
// 创建上下文
|
||||||
|
|
|
@ -39,6 +39,8 @@ func InitSlaveRouter() *gin.Engine {
|
||||||
v3.GET("download/:speed/:path/:name", controllers.SlaveDownload)
|
v3.GET("download/:speed/:path/:name", controllers.SlaveDownload)
|
||||||
// 预览 / 外链
|
// 预览 / 外链
|
||||||
v3.GET("source/:speed/:path/:name", controllers.SlavePreview)
|
v3.GET("source/:speed/:path/:name", controllers.SlavePreview)
|
||||||
|
// 缩略图
|
||||||
|
v3.GET("thumb/:path", controllers.SlaveThumb)
|
||||||
// 删除文件
|
// 删除文件
|
||||||
v3.POST("delete", controllers.SlaveDelete)
|
v3.POST("delete", controllers.SlaveDelete)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,11 @@ type SlaveDownloadService struct {
|
||||||
Speed int `uri:"speed" binding:"min=0"`
|
Speed int `uri:"speed" binding:"min=0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SlaveFileService 从机单文件文件相关服务
|
||||||
|
type SlaveFileService struct {
|
||||||
|
PathEncoded string `uri:"path" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
// SlaveFilesService 从机多文件相关服务
|
// SlaveFilesService 从机多文件相关服务
|
||||||
type SlaveFilesService struct {
|
type SlaveFilesService struct {
|
||||||
Files []string `json:"files" binding:"required,gt=0"`
|
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}
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue