Erweiterungen
This commit is contained in:
@@ -41,8 +41,19 @@ import jakarta.annotation.security.RolesAllowed;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import javax.imageio.IIOImage;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageWriteParam;
|
||||||
|
import javax.imageio.ImageWriter;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
@@ -79,6 +90,8 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver {
|
|||||||
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
|
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
|
||||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy");
|
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy");
|
||||||
private static final int MAX_IMAGE_FILE_SIZE = 32 * 1024 * 1024; // 32 MB aligns with Spring settings
|
private static final int MAX_IMAGE_FILE_SIZE = 32 * 1024 * 1024; // 32 MB aligns with Spring settings
|
||||||
|
private static final int TARGET_IMAGE_WIDTH = 1920;
|
||||||
|
private static final float JPEG_COMPRESSION_QUALITY = 0.8f;
|
||||||
|
|
||||||
public MessageDetailsView(AppUserService appUserService, MessageService messageService,
|
public MessageDetailsView(AppUserService appUserService, MessageService messageService,
|
||||||
SecurityService securityService, MessageBroadcaster messageBroadcaster) {
|
SecurityService securityService, MessageBroadcaster messageBroadcaster) {
|
||||||
@@ -258,8 +271,8 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver {
|
|||||||
|
|
||||||
upload.addSucceededListener(event -> {
|
upload.addSucceededListener(event -> {
|
||||||
try (InputStream inputStream = buffer.getInputStream()) {
|
try (InputStream inputStream = buffer.getInputStream()) {
|
||||||
byte[] bytes = inputStream.readAllBytes();
|
byte[] rawBytes = inputStream.readAllBytes();
|
||||||
if (bytes.length == 0) {
|
if (rawBytes.length == 0) {
|
||||||
base64Ref.set(null);
|
base64Ref.set(null);
|
||||||
preview.setVisible(false);
|
preview.setVisible(false);
|
||||||
confirmButton.setEnabled(false);
|
confirmButton.setEnabled(false);
|
||||||
@@ -268,13 +281,9 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String base64 = Base64.getEncoder().encodeToString(bytes);
|
String processedBase64 = processImageToJpegBase64(rawBytes);
|
||||||
base64Ref.set(base64);
|
if (processedBase64 == null) {
|
||||||
|
base64Ref.set(null);
|
||||||
String mimeType = Optional.ofNullable(event.getMIMEType()).filter(value -> !value.isBlank())
|
|
||||||
.orElseGet(() -> detectImageMimeType(bytes));
|
|
||||||
String dataUrl = createDataUrlFromContent(base64, mimeType);
|
|
||||||
if (dataUrl == null) {
|
|
||||||
preview.setVisible(false);
|
preview.setVisible(false);
|
||||||
confirmButton.setEnabled(false);
|
confirmButton.setEnabled(false);
|
||||||
Notification.show("Das Bild konnte nicht verarbeitet werden.", 3000,
|
Notification.show("Das Bild konnte nicht verarbeitet werden.", 3000,
|
||||||
@@ -282,6 +291,8 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base64Ref.set(processedBase64);
|
||||||
|
String dataUrl = "data:image/jpeg;base64," + processedBase64;
|
||||||
preview.setSrc(dataUrl);
|
preview.setSrc(dataUrl);
|
||||||
preview.setVisible(true);
|
preview.setVisible(true);
|
||||||
confirmButton.setEnabled(true);
|
confirmButton.setEnabled(true);
|
||||||
@@ -517,6 +528,96 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver {
|
|||||||
return "image/png";
|
return "image/png";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String processImageToJpegBase64(byte[] originalBytes) {
|
||||||
|
try {
|
||||||
|
BufferedImage original = ImageIO.read(new ByteArrayInputStream(originalBytes));
|
||||||
|
if (original == null) {
|
||||||
|
log.warn("Bild konnte nicht geladen werden (ImageIO liefert null)");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int originalWidth = original.getWidth();
|
||||||
|
int originalHeight = original.getHeight();
|
||||||
|
if (originalWidth <= 0 || originalHeight <= 0) {
|
||||||
|
log.warn("Ungültige Bildmaße: {}x{}", originalWidth, originalHeight);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int targetWidth = Math.min(originalWidth, TARGET_IMAGE_WIDTH);
|
||||||
|
int targetHeight = (int) Math.round((double) targetWidth / originalWidth * originalHeight);
|
||||||
|
if (targetHeight <= 0) {
|
||||||
|
targetHeight = originalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage scaled = scaleImage(original, targetWidth, targetHeight);
|
||||||
|
byte[] jpegBytes = encodeJpeg(scaled, JPEG_COMPRESSION_QUALITY);
|
||||||
|
if (jpegBytes == null || jpegBytes.length == 0) {
|
||||||
|
log.warn("JPEG-Kodierung lieferte leeres Ergebnis");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Base64.getEncoder().encodeToString(jpegBytes);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
log.error("Fehler beim Skalieren/Konvertieren des Bildes", ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BufferedImage scaleImage(BufferedImage original, int targetWidth, int targetHeight) {
|
||||||
|
if (original.getWidth() == targetWidth && original.getHeight() == targetHeight) {
|
||||||
|
return ensureRgbFormat(original);
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage output = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
|
||||||
|
Graphics2D g2d = output.createGraphics();
|
||||||
|
try {
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
g2d.drawImage(original, 0, 0, targetWidth, targetHeight, null);
|
||||||
|
} finally {
|
||||||
|
g2d.dispose();
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BufferedImage ensureRgbFormat(BufferedImage image) {
|
||||||
|
if (image.getType() == BufferedImage.TYPE_INT_RGB) {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
BufferedImage rgbImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||||
|
Graphics2D g2d = rgbImage.createGraphics();
|
||||||
|
try {
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||||
|
g2d.drawImage(image, 0, 0, null);
|
||||||
|
} finally {
|
||||||
|
g2d.dispose();
|
||||||
|
}
|
||||||
|
return rgbImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] encodeJpeg(BufferedImage image, float quality) throws IOException {
|
||||||
|
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpeg");
|
||||||
|
if (!writers.hasNext()) {
|
||||||
|
throw new IOException("Kein JPEG-Writer verfügbar");
|
||||||
|
}
|
||||||
|
ImageWriter writer = writers.next();
|
||||||
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
|
||||||
|
writer.setOutput(ios);
|
||||||
|
|
||||||
|
ImageWriteParam params = writer.getDefaultWriteParam();
|
||||||
|
if (params.canWriteCompressed()) {
|
||||||
|
params.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||||
|
params.setCompressionQuality(Math.max(0f, Math.min(1f, quality)));
|
||||||
|
}
|
||||||
|
writer.write(null, new IIOImage(image, null, null), params);
|
||||||
|
return baos.toByteArray();
|
||||||
|
} finally {
|
||||||
|
writer.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private UploadI18N createGermanUploadI18n() {
|
private UploadI18N createGermanUploadI18n() {
|
||||||
UploadI18N i18n = new UploadI18N();
|
UploadI18N i18n = new UploadI18N();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user