Files
ares/services/image_service.go
Beyhan Oğur 4d92991817 first commit
2026-04-26 21:30:42 +03:00

191 lines
6.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package services
import (
configs "ares/config"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/gofiber/fiber/v3"
"github.com/h2non/bimg"
"go.uber.org/zap"
)
// ImageOptions defines the parameters for image processing
type ImageOptions struct {
Width int
Height int
Quality int
Format string // "avif", "webp", "png", "jpg"
Folder string // e.g. "settings", "heroes"
}
// ProcessAndSaveImage handles the file upload, processing, and saving
func ProcessAndSaveImage(c fiber.Ctx, fieldName string, opts ImageOptions) (string, error) {
// 1. Get file from form
file, err := c.FormFile(fieldName)
if err != nil {
// If no file uploaded, return empty string (not an error)
configs.Logger.Debug("no file uploaded", zap.Error(err), zap.String("field", fieldName))
return "", nil
}
// 2. Open file
f, err := file.Open()
if err != nil {
configs.Logger.Error("failed to open uploaded file", zap.Error(err), zap.String("filename", file.Filename), zap.String("field", fieldName))
return "", err
}
defer f.Close()
// Read bytes
buffer := make([]byte, file.Size)
n, err := f.Read(buffer)
if err != nil {
configs.Logger.Error("failed to read uploaded file bytes", zap.Error(err), zap.String("filename", file.Filename), zap.Int64("size_expected", file.Size))
return "", err
}
configs.Logger.Debug("read uploaded file bytes", zap.String("filename", file.Filename), zap.Int("read_bytes", n), zap.Int64("size_expected", file.Size))
// 3. Process with bimg
options := bimg.Options{
Width: opts.Width,
Height: opts.Height,
Quality: opts.Quality,
}
// If both Width and Height are set, use Smart Crop (Cover)
if opts.Width > 0 && opts.Height > 0 {
options.Crop = true
options.Gravity = bimg.GravitySmart
}
// Allow enlarging smaller images to requested dimensions and strip metadata
options.Enlarge = true
options.StripMetadata = true
newImage, err := bimg.NewImage(buffer).Process(options)
if err != nil {
configs.Logger.Error("image processing failed", zap.Error(err), zap.Any("options", options))
return "", fmt.Errorf("resim işleme hatası: %v", err)
}
// 4. Convert Format (if requested)
// Default to AVIF if not specified
targetFormat := bimg.AVIF
ext := ".avif"
if opts.Format != "" {
switch strings.ToLower(opts.Format) {
case "webp":
targetFormat = bimg.WEBP
ext = ".webp"
case "png":
targetFormat = bimg.PNG
ext = ".png"
case "jpg", "jpeg":
targetFormat = bimg.JPEG
ext = ".jpg"
case "avif":
targetFormat = bimg.AVIF
ext = ".avif"
}
}
if newImage, err = bimg.NewImage(newImage).Convert(targetFormat); err != nil {
configs.Logger.Error("format conversion failed", zap.Error(err), zap.String("target_format", strings.ToLower(opts.Format)), zap.String("ext", ext))
return "", fmt.Errorf("format dönüştürme hatası: %v", err)
}
// 5. Generate Filename and Path
filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
// Ensure uploads directory exists
// We save to ./uploads/{folder} (root uploads, served by main.go handler)
uploadPath := filepath.Join("uploads", opts.Folder)
if err := os.MkdirAll(uploadPath, 0755); err != nil {
configs.Logger.Error("failed to create upload directory", zap.Error(err), zap.String("upload_path", uploadPath))
return "", fmt.Errorf("dizin oluşturma hatası: %v", err)
}
fullPath := filepath.Join(uploadPath, filename)
// 6. Save File using bimg
if err := bimg.Write(fullPath, newImage); err != nil {
configs.Logger.Error("failed to write image to disk", zap.Error(err), zap.String("full_path", fullPath))
return "", fmt.Errorf("dosya kaydetme hatası: %v", err)
}
configs.Logger.Info("image saved", zap.String("path", fullPath), zap.String("url", fmt.Sprintf("/uploads/%s/%s", opts.Folder, filename)), zap.Int("width", opts.Width), zap.Int("height", opts.Height), zap.String("format", opts.Format), zap.Int("quality", opts.Quality))
// Return relative path for DB (e.g. /uploads/heroes/123.avif)
return fmt.Sprintf("/uploads/%s/%s", opts.Folder, filename), nil
}
// ProcessAndSaveImageFromBytes processes raw image bytes and saves the file similar to ProcessAndSaveImage
func ProcessAndSaveImageFromBytes(buffer []byte, opts ImageOptions) (string, error) {
configs.Logger.Debug("ProcessAndSaveImageFromBytes called", zap.Int("input_bytes", len(buffer)), zap.Any("options", opts))
// 1. Process with bimg
options := bimg.Options{
Width: opts.Width,
Height: opts.Height,
Quality: opts.Quality,
}
if opts.Width > 0 && opts.Height > 0 {
options.Crop = true
options.Gravity = bimg.GravitySmart
}
options.Enlarge = true
options.StripMetadata = true
newImage, err := bimg.NewImage(buffer).Process(options)
if err != nil {
configs.Logger.Error("image processing from bytes failed", zap.Error(err), zap.Any("options", options))
return "", fmt.Errorf("resim işleme hatası: %v", err)
}
// Convert format
targetFormat := bimg.AVIF
ext := ".avif"
if opts.Format != "" {
switch strings.ToLower(opts.Format) {
case "webp":
targetFormat = bimg.WEBP
ext = ".webp"
case "png":
targetFormat = bimg.PNG
ext = ".png"
case "jpg", "jpeg":
targetFormat = bimg.JPEG
ext = ".jpg"
case "avif":
targetFormat = bimg.AVIF
ext = ".avif"
}
}
if newImage, err = bimg.NewImage(newImage).Convert(targetFormat); err != nil {
configs.Logger.Error("format conversion from bytes failed", zap.Error(err), zap.String("target_format", strings.ToLower(opts.Format)), zap.String("ext", ext))
return "", fmt.Errorf("format dönüştürme hatası: %v", err)
}
filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
uploadPath := filepath.Join("uploads", opts.Folder)
if err := os.MkdirAll(uploadPath, 0755); err != nil {
configs.Logger.Error("failed to create upload directory (from bytes)", zap.Error(err), zap.String("upload_path", uploadPath))
return "", fmt.Errorf("dizin oluşturma hatası: %v", err)
}
fullPath := filepath.Join(uploadPath, filename)
if err := bimg.Write(fullPath, newImage); err != nil {
configs.Logger.Error("failed to write image to disk (from bytes)", zap.Error(err), zap.String("full_path", fullPath))
return "", fmt.Errorf("dosya kaydetme hatası: %v", err)
}
configs.Logger.Info("image saved from bytes", zap.String("path", fullPath), zap.String("url", fmt.Sprintf("/uploads/%s/%s", opts.Folder, filename)), zap.Int("width", opts.Width), zap.Int("height", opts.Height), zap.String("format", opts.Format), zap.Int("quality", opts.Quality))
return fmt.Sprintf("/uploads/%s/%s", opts.Folder, filename), nil
}