file: add taskqueue worker process image thumbnail
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 30s
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 30s
This commit is contained in:
196
file/worker/imageprocessworker.go
Normal file
196
file/worker/imageprocessworker.go
Normal file
@ -0,0 +1,196 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"godemo/file/file"
|
||||
"godemo/file/internal/svc"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
_ "image/png"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
// 图片处理任务
|
||||
type ImageProcessTask struct {
|
||||
FileID string // 原始文件ID
|
||||
Versions []file.ImageVersion // 需要生成的版本
|
||||
RequestID string // 请求ID(用于追踪)
|
||||
CreatedAt time.Time // 创建时间
|
||||
}
|
||||
|
||||
var ImageProcessTaskQueue chan ImageProcessTask
|
||||
|
||||
func InitImageProcessTaskQueue(bufferSize int) {
|
||||
ImageProcessTaskQueue = make(chan ImageProcessTask, bufferSize)
|
||||
}
|
||||
|
||||
func StartImageProcessWorkers(workerCount int, svcCtx *svc.ServiceContext) {
|
||||
for i := 0; i < workerCount; i++ {
|
||||
go func(workerID int) {
|
||||
for task := range ImageProcessTaskQueue {
|
||||
logx.Infof("Worker %d processing task for FileID: %s", workerID, task.FileID)
|
||||
if err := processImage(task, svcCtx); err != nil {
|
||||
logx.Errorf("Worker %d failed to process FileID: %s, err: %v", workerID, task.FileID, err)
|
||||
}
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
}
|
||||
|
||||
func processImage(task ImageProcessTask, svcCtx *svc.ServiceContext) error {
|
||||
// 加载原始图片(示意,假设你实现了这类方法)
|
||||
originalImage, err := LoadImageFromMinIO(svcCtx.MinioClient, task.FileID, file.ImageVersion_original)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 如果是 ALL,则转化成 THUMBNAIL + PREVIEW
|
||||
versions := task.Versions
|
||||
if len(versions) == 1 && versions[0] == file.ImageVersion_all {
|
||||
versions = []file.ImageVersion{file.ImageVersion_thumbnail, file.ImageVersion_preview}
|
||||
}
|
||||
|
||||
for _, version := range versions {
|
||||
switch version {
|
||||
case file.ImageVersion_thumbnail:
|
||||
err := generateAndUpload(originalImage, task.FileID, file.ImageVersion_thumbnail, svcCtx)
|
||||
if err != nil {
|
||||
logx.Errorf("Generate thumbnail failed: %v", err)
|
||||
}
|
||||
case file.ImageVersion_preview:
|
||||
err := generateAndUpload(originalImage, task.FileID, file.ImageVersion_preview, svcCtx)
|
||||
if err != nil {
|
||||
logx.Errorf("Generate preview failed: %v", err)
|
||||
}
|
||||
default:
|
||||
logx.Infof("Unsupported image version: %v", version)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateAndUpload(originalImage []byte, fileID string, version file.ImageVersion, svcCtx *svc.ServiceContext) error {
|
||||
// 示例尺寸(你可以从配置或常量读取)
|
||||
var width, height int
|
||||
switch version {
|
||||
case file.ImageVersion_thumbnail:
|
||||
width, height = 150, 150
|
||||
case file.ImageVersion_preview:
|
||||
width, height = 800, 600
|
||||
}
|
||||
|
||||
// resize(伪代码:请换成你自己的图像处理库,如 imaging)
|
||||
resizedImage, err := ResizeImage(originalImage, width, height)
|
||||
if err != nil {
|
||||
logx.Errorf("generateAndUpload failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 上传到 MinIO
|
||||
return UploadImageToMinIO(svcCtx.MinioClient, fileID, version, resizedImage)
|
||||
}
|
||||
|
||||
func LoadImageFromMinIO(client *minio.Client, fileID string, version file.ImageVersion) ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
bucket, objectKey, _ := GetBucketAndObjectKey(fileID, version)
|
||||
|
||||
object, err := client.GetObject(ctx, bucket, objectKey, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get object from MinIO: %w", err)
|
||||
}
|
||||
defer object.Close()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err := io.Copy(buf, object); err != nil {
|
||||
return nil, fmt.Errorf("failed to read object: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func GetBucketAndObjectKey(fileId string, version file.ImageVersion) (string, string, error) {
|
||||
bucket := version.String()
|
||||
objectKey := fileId
|
||||
if version == file.ImageVersion_original {
|
||||
// fileID = xx/yy/cc/ll.png 获取第一段作为bucket
|
||||
bucket, objectKey, _ = GetBucketAndObjectKeyFromFileID(fileId)
|
||||
}
|
||||
return bucket, objectKey, nil
|
||||
}
|
||||
|
||||
func GetBucketAndObjectKeyFromFileID(fileID string) (string, string, error) {
|
||||
idx := strings.Index(fileID, "/")
|
||||
if idx == -1 {
|
||||
return "", "", fmt.Errorf("invalid fileID: %s", fileID)
|
||||
}
|
||||
return fileID[:idx], fileID[idx+1:], nil
|
||||
}
|
||||
|
||||
func ResizeImage(original []byte, width, height int) ([]byte, error) {
|
||||
img, _, err := image.Decode(bytes.NewReader(original))
|
||||
if err != nil {
|
||||
logx.Errorf("ResizeImage failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
srcBounds := img.Bounds()
|
||||
srcWidth := srcBounds.Dx()
|
||||
srcHeight := srcBounds.Dy()
|
||||
|
||||
if srcWidth == 0 || srcHeight == 0 {
|
||||
return nil, errors.New("source image has zero size")
|
||||
}
|
||||
|
||||
// 创建缩放后的图像(RGBA 格式)
|
||||
dst := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
|
||||
// 最近邻缩放
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
srcX := x * srcWidth / width
|
||||
srcY := y * srcHeight / height
|
||||
color := img.At(srcX+srcBounds.Min.X, srcY+srcBounds.Min.Y)
|
||||
dst.Set(x, y, color)
|
||||
}
|
||||
}
|
||||
|
||||
// 编码为 JPEG
|
||||
var buf bytes.Buffer
|
||||
err = jpeg.Encode(&buf, dst, &jpeg.Options{Quality: 85})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func UploadImageToMinIO(client *minio.Client, fileID string, version file.ImageVersion, data []byte) error {
|
||||
bucket, objectKey, err := GetBucketAndObjectKey(fileID, version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err = client.PutObject(ctx, bucket, objectKey, bytes.NewReader(data), int64(len(data)), minio.PutObjectOptions{
|
||||
ContentType: "image/jpeg",
|
||||
})
|
||||
logx.Infof("Uploaded image to MinIO: %s/%s", bucket, objectKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upload to MinIO failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user