157 lines
4.5 KiB
Go
157 lines
4.5 KiB
Go
package models
|
||
|
||
import (
|
||
"fmt"
|
||
"image"
|
||
"io"
|
||
"log"
|
||
"net/http"
|
||
"os"
|
||
"path/filepath"
|
||
"regexp"
|
||
|
||
"github.com/chai2010/webp"
|
||
"github.com/disintegration/imaging"
|
||
giftowebp "github.com/sizeofint/gif-to-webp"
|
||
)
|
||
|
||
// Image is a struct that contains the information of an image
|
||
type Image struct {
|
||
image image.Image
|
||
format string
|
||
data []byte
|
||
}
|
||
|
||
// 初始化图片
|
||
func (img *Image) Init(content string) error {
|
||
|
||
if len(regexp.MustCompile(`image.gameuiux.cn`).FindStringSubmatch(content)) > 0 {
|
||
key := regexp.MustCompile(`^https?://image.gameuiux.cn/`).ReplaceAllString(content, "")
|
||
filePath := filepath.Join("oss", key)
|
||
|
||
// 先检查本地是否存在原图, 没有则从oss下载
|
||
if _, err := os.Stat(filePath); err != nil {
|
||
|
||
ensureDirExists := func(filePath string) error {
|
||
dir := filepath.Dir(filePath) // 获取文件所在的目录
|
||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||
// 如果目录不存在,创建多级目录
|
||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 确保目录存在
|
||
if err := ensureDirExists(filePath); err != nil {
|
||
log.Fatalf("Failed to create directory: %v\n", err)
|
||
}
|
||
|
||
bucket := GetBucket("gameui-image2")
|
||
fmt.Println("从 OSS 下载:", key, filePath)
|
||
err = bucket.GetObjectToFile(key, filePath)
|
||
if err != nil {
|
||
log.Println("Failed to download file: ", err)
|
||
return err
|
||
}
|
||
}
|
||
|
||
// 从本地打开文件
|
||
body, err := os.Open(filePath)
|
||
if err != nil {
|
||
log.Println("打开本地图片失败:", err)
|
||
return err
|
||
}
|
||
defer body.Close()
|
||
|
||
// 判断图片格式是否为 gif 或 GIF
|
||
if len(regexp.MustCompile(`\.gif$`).FindStringSubmatch(key)) > 0 {
|
||
img.format = "gif"
|
||
img.data, err = io.ReadAll(body)
|
||
|
||
if err != nil {
|
||
log.Println("读取图片失败", err)
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 将文件解码为 image.Image
|
||
img.image, img.format, err = image.Decode(body)
|
||
if err != nil {
|
||
log.Println("解码图像失败", err)
|
||
return err
|
||
}
|
||
return nil
|
||
} else {
|
||
var resp *http.Response
|
||
log.Println("直接从网络下载图片:", content)
|
||
resp, err := http.Get(content)
|
||
if err != nil {
|
||
log.Println("下载图片失败", err)
|
||
return err
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 判断图片格式是否为 gif 或 GIF
|
||
if len(regexp.MustCompile(`\.gif$`).FindStringSubmatch(content)) > 0 {
|
||
img.format = "gif"
|
||
img.data, err = io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
log.Println("读取图片失败", err)
|
||
return err
|
||
}
|
||
println("数据长度:", len(img.data))
|
||
return nil
|
||
}
|
||
|
||
// 将文件解码为 image.Image
|
||
img.image, img.format, err = image.Decode(resp.Body)
|
||
if err != nil {
|
||
log.Println("解码图像失败", err)
|
||
return nil
|
||
}
|
||
return nil
|
||
}
|
||
}
|
||
|
||
// 将图片输出为指定尺寸的 webp 格式(默认使用 Lanczos 缩放算法)
|
||
func (img *Image) ToWebP(width int, height int, fit string) ([]byte, error) {
|
||
// 如果原图是GIF格式的动态图片,直接不作尺寸处理,直接转换为webp格式
|
||
if img.format == "gif" {
|
||
converter := giftowebp.NewConverter()
|
||
converter.LoopCompatibility = true // 是否兼容循环动画
|
||
converter.WebPConfig.SetLossless(1) // 0 有损压缩 1无损压缩
|
||
converter.WebPConfig.SetMethod(6) // 压缩速度 0-6 0最快 6质量最好
|
||
converter.WebPConfig.SetQuality(100) // 压缩质量 0-100
|
||
converter.WebPAnimEncoderOptions.SetKmin(9)
|
||
converter.WebPAnimEncoderOptions.SetKmax(17)
|
||
return converter.Convert(img.data)
|
||
}
|
||
// 如果指定了宽高却没有指定fit, 则默认fit为cover
|
||
if width != 0 && height != 0 && fit == "" {
|
||
fit = "cover"
|
||
}
|
||
// 如果未指定宽高和fit, 则不缩放图片直接返回webp
|
||
if width == 0 && height == 0 && fit == "" {
|
||
return webp.EncodeRGBA(img.image, 100)
|
||
}
|
||
switch fit {
|
||
case "cover":
|
||
return webp.EncodeRGBA(imaging.Fill(img.image, width, height, imaging.Center, imaging.Lanczos), 100)
|
||
case "contain":
|
||
return webp.EncodeRGBA(imaging.Fit(img.image, width, height, imaging.Lanczos), 100)
|
||
case "fill":
|
||
return webp.EncodeRGBA(imaging.Fill(img.image, width, height, imaging.Center, imaging.Lanczos), 100)
|
||
case "inside":
|
||
return webp.EncodeRGBA(imaging.Fit(img.image, width, height, imaging.Lanczos), 100)
|
||
case "outside":
|
||
return webp.EncodeRGBA(imaging.Fill(img.image, width, height, imaging.Center, imaging.Lanczos), 100)
|
||
case "scale-down":
|
||
return webp.EncodeRGBA(imaging.Fit(img.image, width, height, imaging.Lanczos), 100)
|
||
default:
|
||
return webp.EncodeRGBA(imaging.Resize(img.image, width, height, imaging.Lanczos), 100)
|
||
}
|
||
}
|