You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cloudreve/pkg/filesystem/image.go

142 lines
4.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package filesystem
import (
"context"
"fmt"
"strconv"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/conf"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/response"
"github.com/cloudreve/Cloudreve/v3/pkg/thumb"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/rwcarlsen/goexif/exif"
)
/* ================
图像处理相关
================
*/
// HandledExtension 可以生成缩略图的文件扩展名
var HandledExtension = []string{"jpg", "jpeg", "png", "gif"}
// GetThumb 获取文件的缩略图
func (fs *FileSystem) GetThumb(ctx context.Context, id uint) (*response.ContentResponse, error) {
// 根据 ID 查找文件
err := fs.resetFileIDIfNotExist(ctx, id)
if err != nil || fs.FileTarget[0].PicInfo == "" {
return &response.ContentResponse{
Redirect: false,
}, ErrObjectNotExist
}
w, h := fs.GenerateThumbnailSize(0, 0)
ctx = context.WithValue(ctx, fsctx.ThumbSizeCtx, [2]uint{w, h})
ctx = context.WithValue(ctx, fsctx.FileModelCtx, fs.FileTarget[0])
res, err := fs.Handler.Thumb(ctx, fs.FileTarget[0].SourceName)
if err == nil && conf.SystemConfig.Mode == "master" {
res.MaxAge = model.GetIntSetting("preview_timeout", 60)
}
// 出错时重新生成缩略图
if err != nil {
fs.GenerateThumbnail(ctx, &fs.FileTarget[0])
}
return res, err
}
// GenerateThumbnail 尝试为本地策略文件生成缩略图并获取图像原始大小
// TODO 失败时,如果之前还有图像信息,则清除
func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) {
// 判断是否可以生成缩略图
if !IsInExtensionList(HandledExtension, file.Name) {
return
}
// 新建上下文
newCtx, cancel := context.WithCancel(context.Background())
defer cancel()
// 获取文件数据
source, err := fs.Handler.Get(newCtx, file.SourceName)
if err != nil {
return
}
defer source.Close()
image, err := thumb.NewThumbFromFile(source, file.Name)
if err != nil {
util.Log().Warning("生成缩略图时无法解析 [%s] 图像数据:%s", file.SourceName, err)
return
}
// 获取原始图像尺寸
w, h := image.GetSize()
// 生成缩略图
image.GetThumb(fs.GenerateThumbnailSize(w, h))
// 保存到文件
err = image.Save(util.RelativePath(file.SourceName + conf.ThumbConfig.FileSuffix))
if err != nil {
util.Log().Warning("无法保存缩略图:%s", err)
return
}
// 更新文件的图像信息
if file.Model.ID > 0 {
err = file.UpdatePicInfo(fmt.Sprintf("%d,%d", w, h))
} else {
file.PicInfo = fmt.Sprintf("%d,%d", w, h)
}
// 更新文件的图像信息
// 记录文件句柄位置并还原, 获取 exif 信息
currentPosition, err := source.Seek(0, 1)
source.Seek(0,0)
x, err := exif.Decode(source)
source.Seek(currentPosition, 0)
if err != nil {
util.Log().Warning("照片解析EXIF失败%s", err)
}else{
ExifCamModel, _ := x.Get(exif.Model)
file.ExifModel,_ = ExifCamModel.StringVal()
ExifDateTime, _ := x.DateTime()
if !ExifDateTime.IsZero() {
file.ExifDateTime = ExifDateTime
}
lat, long, _ := x.LatLong()
if lat > 0 && long > 0 {
file.ExifLatLong = fmt.Sprintf("%f,%f", lat, long)
}
util.Log().Debug("照片的经纬度:%f,%f", lat,long)
if file.Model.ID > 0 {
file.UpdatePicExifModel(file.ExifModel)
if !ExifDateTime.IsZero() {
file.UpdatePicExifDateTime(file.ExifDateTime)
}
if lat > 0 && long > 0 {
file.UpdatePicExifLatLong(file.ExifLatLong)
}
}
}
// 失败时删除缩略图文件
if err != nil {
_, _ = fs.Handler.Delete(newCtx, []string{file.SourceName + conf.ThumbConfig.FileSuffix})
}
}
// GenerateThumbnailSize 获取要生成的缩略图的尺寸
func (fs *FileSystem) GenerateThumbnailSize(w, h int) (uint, uint) {
if conf.SystemConfig.Mode == "master" {
options := model.GetSettingByNames("thumb_width", "thumb_height")
w, _ := strconv.ParseUint(options["thumb_width"], 10, 32)
h, _ := strconv.ParseUint(options["thumb_height"], 10, 32)
return uint(w), uint(h)
}
return conf.ThumbConfig.MaxWidth, conf.ThumbConfig.MaxHeight
}