126 lines
3.2 KiB
Go
126 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/h2non/bimg"
|
|
"github.com/minio/minio-go/v7"
|
|
)
|
|
|
|
func ProcessImage(ctx context.Context, client *minio.Client, bucket, key string) error {
|
|
stat, err := client.StatObject(ctx, bucket, key, minio.StatObjectOptions{})
|
|
if err != nil {
|
|
log.Printf("🚨 Failed to stat object: %s, error: %v", key, err)
|
|
return err
|
|
}
|
|
|
|
contentType := stat.ContentType
|
|
if !strings.HasPrefix(contentType, "image/") {
|
|
log.Printf("⏭️ Skipping non-image file: %s", key)
|
|
return nil
|
|
}
|
|
|
|
if processed := stat.UserMetadata["X-Amz-Meta-Processed"]; processed == "true" {
|
|
log.Printf("♻️ Already processed: %s", key)
|
|
return nil
|
|
}
|
|
|
|
if processed := stat.UserMetadata["Processed"]; processed == "true" {
|
|
log.Printf("♻️ Already processed: %s", key)
|
|
return nil
|
|
}
|
|
|
|
object, err := client.GetObject(ctx, bucket, key, minio.GetObjectOptions{})
|
|
if err != nil {
|
|
log.Printf("🚨 Failed to get object: %s, error: %v", key, err)
|
|
return err
|
|
}
|
|
defer object.Close()
|
|
|
|
sourceBuffer := new(bytes.Buffer)
|
|
_, err = sourceBuffer.ReadFrom(object)
|
|
if err != nil {
|
|
log.Printf("🚨 Failed to read object: %s, error: %v", key, err)
|
|
return err
|
|
}
|
|
|
|
img := bimg.NewImage(sourceBuffer.Bytes())
|
|
|
|
fileName := filepath.Base(key)
|
|
filePath := path.Dir(key)
|
|
|
|
meta := map[string]string{
|
|
"Processed": "true",
|
|
}
|
|
|
|
// Generate optimized JPEG: just convert without resizing, keep quality high
|
|
jpegBuf, err := img.Process(bimg.Options{
|
|
Quality: 90,
|
|
Type: bimg.JPEG,
|
|
StripMetadata: true,
|
|
NoAutoRotate: false,
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate optimized jpeg: %w", err)
|
|
}
|
|
|
|
// Generate optimized WebP: just convert without resizing, keep quality high
|
|
webpBuf, err := img.Process(bimg.Options{
|
|
Quality: 90,
|
|
Type: bimg.WEBP,
|
|
StripMetadata: true,
|
|
NoAutoRotate: false,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate webp: %w", err)
|
|
}
|
|
|
|
// Generate thumbnail: resize to 400px width
|
|
thumbBuf, err := img.Process(bimg.Options{
|
|
Width: 400,
|
|
Quality: 85,
|
|
Type: bimg.JPEG,
|
|
StripMetadata: true,
|
|
NoAutoRotate: false,
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate thumbnail: %w", err)
|
|
}
|
|
|
|
// Upload optimized JPEG
|
|
err = UploadToMinIO(ctx, client, bucket, path.Join(filePath, "optimized", fileName), "image/jpeg", jpegBuf, meta)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to upload optimized jpeg: %w", err)
|
|
}
|
|
|
|
// Upload WebP
|
|
webpName := strings.TrimSuffix(fileName, filepath.Ext(fileName)) + ".webp"
|
|
err = UploadToMinIO(ctx, client, bucket, path.Join(filePath, "webp", webpName), "image/webp", webpBuf, meta)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to upload webp image: %w", err)
|
|
}
|
|
|
|
// Upload thumbnail
|
|
err = UploadToMinIO(ctx, client, bucket, path.Join(filePath, "thumbs", fileName), "image/jpeg", thumbBuf, meta)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to upload thumbnail: %w", err)
|
|
}
|
|
|
|
// Reupload original with processed=true metadata (unchanged)
|
|
err = UploadToMinIO(ctx, client, bucket, key, contentType, sourceBuffer.Bytes(), meta)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to reupload original image: %w", err)
|
|
}
|
|
|
|
log.Printf("✅ Image processed: %s", key)
|
|
return nil
|
|
}
|