mirror of
https://github.com/halejohn/Cloudreve.git
synced 2026-01-26 17:41:57 +08:00
refactor(thumb): thumb logic for slave policy
This commit is contained in:
@@ -11,7 +11,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorThumbNotExist = fmt.Errorf("thumb not exist")
|
||||
ErrorThumbNotExist = fmt.Errorf("thumb not exist")
|
||||
ErrorThumbNotSupported = fmt.Errorf("thumb not supported")
|
||||
)
|
||||
|
||||
// Handler 存储策略适配器
|
||||
@@ -28,6 +29,9 @@ type Handler interface {
|
||||
|
||||
// 获取缩略图,可直接在ContentResponse中返回文件数据流,也可指
|
||||
// 定为重定向
|
||||
// 如果缩略图不存在, 且需要 Cloudreve 代理生成并上传,应返回 ErrorThumbNotExist,生
|
||||
// 成的缩略图文件存储规则与本机策略一致。
|
||||
// 如果不支持此文件的缩略图,并且不希望后续继续请求此缩略图,应返回 ErrorThumbNotSupported
|
||||
Thumb(ctx context.Context, file *model.File) (*response.ContentResponse, error)
|
||||
|
||||
// 获取外链/下载地址,
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/auth"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/conf"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/response"
|
||||
@@ -196,13 +197,18 @@ func (handler Driver) Delete(ctx context.Context, files []string) ([]string, err
|
||||
|
||||
// Thumb 获取文件缩略图
|
||||
func (handler Driver) Thumb(ctx context.Context, file *model.File) (*response.ContentResponse, error) {
|
||||
if file.MetadataSerialized[model.ThumbStatusMetadataKey] == model.ThumbStatusNotExist {
|
||||
// Quick check thumb existence on master.
|
||||
if conf.SystemConfig.Mode == "master" && file.MetadataSerialized[model.ThumbStatusMetadataKey] == model.ThumbStatusNotExist {
|
||||
// Tell invoker to generate a thumb
|
||||
return nil, driver.ErrorThumbNotExist
|
||||
}
|
||||
|
||||
thumbFile, err := handler.Get(ctx, file.SourceName+model.GetSettingByNameWithDefault("thumb_file_suffix", "._thumb"))
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
err = fmt.Errorf("thumb not exist: %w (%w)", err, driver.ErrorThumbNotExist)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,18 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/auth"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/response"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/request"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
)
|
||||
|
||||
// Driver 远程存储策略适配器
|
||||
@@ -205,8 +208,18 @@ func (handler *Driver) Delete(ctx context.Context, files []string) ([]string, er
|
||||
|
||||
// Thumb 获取文件缩略图
|
||||
func (handler *Driver) Thumb(ctx context.Context, file *model.File) (*response.ContentResponse, error) {
|
||||
// quick check by extensions
|
||||
supported := []string{"png", "jpg", "jpeg", "gif"}
|
||||
if len(handler.Policy.OptionsSerialized.ThumbExts) > 0 {
|
||||
supported = handler.Policy.OptionsSerialized.ThumbExts
|
||||
}
|
||||
|
||||
if !util.IsInExtensionList(supported, file.Name) {
|
||||
return nil, driver.ErrorThumbNotSupported
|
||||
}
|
||||
|
||||
sourcePath := base64.RawURLEncoding.EncodeToString([]byte(file.SourceName))
|
||||
thumbURL := handler.getAPIUrl("thumb") + "/" + sourcePath
|
||||
thumbURL := fmt.Sprintf("%s/%s/%s", handler.getAPIUrl("thumb"), sourcePath, filepath.Ext(file.Name))
|
||||
ttl := model.GetIntSetting("preview_timeout", 60)
|
||||
signedThumbURL, err := auth.SignURI(handler.AuthInstance, thumbURL, int64(ttl))
|
||||
if err != nil {
|
||||
|
||||
@@ -64,10 +64,6 @@ func (fs *FileSystem) AddFile(ctx context.Context, parent *model.Folder, file fs
|
||||
UploadSessionID: uploadInfo.UploadSessionID,
|
||||
}
|
||||
|
||||
if fs.Policy.IsThumbExist(uploadInfo.FileName) {
|
||||
newFile.PicInfo = "1,1"
|
||||
}
|
||||
|
||||
err = newFile.Create()
|
||||
|
||||
if err != nil {
|
||||
@@ -97,9 +93,10 @@ func (fs *FileSystem) GetPhysicalFileContent(ctx context.Context, path string) (
|
||||
}
|
||||
|
||||
// Preview 预览文件
|
||||
// path - 文件虚拟路径
|
||||
// isText - 是否为文本文件,文本文件会忽略重定向,直接由
|
||||
// 服务端拉取中转给用户,故会对文件大小进行限制
|
||||
//
|
||||
// path - 文件虚拟路径
|
||||
// isText - 是否为文本文件,文本文件会忽略重定向,直接由
|
||||
// 服务端拉取中转给用户,故会对文件大小进行限制
|
||||
func (fs *FileSystem) Preview(ctx context.Context, id uint, isText bool) (*response.ContentResponse, error) {
|
||||
err := fs.resetFileIDIfNotExist(ctx, id)
|
||||
if err != nil {
|
||||
|
||||
@@ -177,23 +177,12 @@ func GenericAfterUpdate(ctx context.Context, fs *FileSystem, newFile fsctx.FileH
|
||||
// SlaveAfterUpload Slave模式下上传完成钩子
|
||||
func SlaveAfterUpload(session *serializer.UploadSession) Hook {
|
||||
return func(ctx context.Context, fs *FileSystem, fileHeader fsctx.FileHeader) error {
|
||||
fileInfo := fileHeader.Info()
|
||||
|
||||
// 构造一个model.File,用于生成缩略图
|
||||
file := model.File{
|
||||
Name: fileInfo.FileName,
|
||||
SourceName: fileInfo.SavePath,
|
||||
}
|
||||
|
||||
if session.Callback == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 发送回调请求
|
||||
callbackBody := serializer.UploadCallback{
|
||||
PicInfo: file.PicInfo,
|
||||
}
|
||||
|
||||
callbackBody := serializer.UploadCallback{}
|
||||
return cluster.RemoteCallback(session.Callback, callbackBody)
|
||||
}
|
||||
}
|
||||
@@ -268,10 +257,6 @@ func HookPopPlaceholderToFile(picInfo string) Hook {
|
||||
return func(ctx context.Context, fs *FileSystem, fileHeader fsctx.FileHeader) error {
|
||||
fileInfo := fileHeader.Info()
|
||||
fileModel := fileInfo.Model.(*model.File)
|
||||
if picInfo == "" && fs.Policy.IsThumbExist(fileInfo.FileName) {
|
||||
picInfo = "1,1"
|
||||
}
|
||||
|
||||
return fileModel.PopChunkToFile(fileInfo.LastModified, picInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ func (fs *FileSystem) GetThumb(ctx context.Context, id uint) (*response.ContentR
|
||||
// Regenerate thumb if the thumb is not initialized yet
|
||||
fs.GenerateThumbnail(ctx, &fs.FileTarget[0])
|
||||
res, err = fs.Handler.Thumb(ctx, &fs.FileTarget[0])
|
||||
} else if errors.Is(err, driver.ErrorThumbNotSupported) {
|
||||
// Policy handler explicitly indicates thumb not available
|
||||
_ = updateThumbStatus(&fs.FileTarget[0], model.ThumbStatusNotAvailable)
|
||||
}
|
||||
|
||||
if err == nil && conf.SystemConfig.Mode == "master" {
|
||||
@@ -157,6 +160,10 @@ func updateThumbStatus(file *model.File, status string) error {
|
||||
model.ThumbStatusMetadataKey: status,
|
||||
})
|
||||
} else {
|
||||
if file.MetadataSerialized == nil {
|
||||
file.MetadataSerialized = map[string]string{}
|
||||
}
|
||||
|
||||
file.MetadataSerialized[model.ThumbStatusMetadataKey] = status
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package filesystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
@@ -63,20 +62,5 @@ func (fs *FileSystem) ValidateExtension(ctx context.Context, fileName string) bo
|
||||
return true
|
||||
}
|
||||
|
||||
return IsInExtensionList(fs.Policy.OptionsSerialized.FileType, fileName)
|
||||
}
|
||||
|
||||
// IsInExtensionList 返回文件的扩展名是否在给定的列表范围内
|
||||
func IsInExtensionList(extList []string, fileName string) bool {
|
||||
ext := strings.ToLower(filepath.Ext(fileName))
|
||||
// 无扩展名时
|
||||
if len(ext) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if util.ContainsString(extList, ext[1:]) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return util.IsInExtensionList(fs.Policy.OptionsSerialized.FileType, fileName)
|
||||
}
|
||||
|
||||
@@ -39,9 +39,7 @@ func NewThumbFromFile(file io.Reader, name string) (*Thumb, error) {
|
||||
var err error
|
||||
var img image.Image
|
||||
switch ext[1:] {
|
||||
case "jpg":
|
||||
img, err = jpeg.Decode(file)
|
||||
case "jpeg":
|
||||
case "jpg", "jpeg":
|
||||
img, err = jpeg.Decode(file)
|
||||
case "gif":
|
||||
img, err = gif.Decode(file)
|
||||
|
||||
@@ -2,6 +2,7 @@ package util
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -32,6 +33,21 @@ func ContainsUint(s []uint, e uint) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInExtensionList 返回文件的扩展名是否在给定的列表范围内
|
||||
func IsInExtensionList(extList []string, fileName string) bool {
|
||||
ext := strings.ToLower(filepath.Ext(fileName))
|
||||
// 无扩展名时
|
||||
if len(ext) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if ContainsString(extList, ext[1:]) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsString 返回list中是否包含
|
||||
func ContainsString(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
|
||||
Reference in New Issue
Block a user