feat(recipe): compress hero image to 400px preview on save
Adds Thumbnailator-based ImageCompressor that resizes uploaded images to a 400px-wide JPEG preview stored in hero_image_preview. The recipe list uses the preview instead of the full image URL. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
package com.recipeapp.recipe;
|
||||
|
||||
import net.coobird.thumbnailator.Thumbnails;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Base64;
|
||||
|
||||
@Component
|
||||
public class ImageCompressor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ImageCompressor.class);
|
||||
|
||||
private static final int PREVIEW_WIDTH = 400;
|
||||
private static final double PREVIEW_QUALITY = 0.6;
|
||||
private static final String DATA_URI_PREFIX = "data:image/";
|
||||
private static final String BASE64_MARKER = ";base64,";
|
||||
private static final String OUTPUT_PREFIX = "data:image/jpeg;base64,";
|
||||
|
||||
public String compressToPreview(String dataUri) {
|
||||
if (dataUri == null || dataUri.isBlank()) return null;
|
||||
if (!dataUri.startsWith(DATA_URI_PREFIX)) return null;
|
||||
|
||||
int markerIdx = dataUri.indexOf(BASE64_MARKER);
|
||||
if (markerIdx < 0) return null;
|
||||
|
||||
byte[] imageBytes;
|
||||
try {
|
||||
imageBytes = Base64.getDecoder().decode(dataUri.substring(markerIdx + BASE64_MARKER.length()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
BufferedImage original = ImageIO.read(new ByteArrayInputStream(imageBytes));
|
||||
if (original == null) {
|
||||
log.warn("ImageIO could not decode image — unsupported format (data URI prefix: {})",
|
||||
dataUri.substring(0, Math.min(dataUri.indexOf(',') + 1, 40)));
|
||||
return null;
|
||||
}
|
||||
|
||||
int targetWidth = Math.min(original.getWidth(), PREVIEW_WIDTH);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
Thumbnails.of(original)
|
||||
.width(targetWidth)
|
||||
.outputFormat("jpeg")
|
||||
.outputQuality(PREVIEW_QUALITY)
|
||||
.toOutputStream(bos);
|
||||
return OUTPUT_PREFIX + Base64.getEncoder().encodeToString(bos.toByteArray());
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to generate image preview", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user