Imrpoved image handling since the size of the image is about 5MB.
This commit is contained in:
@@ -124,12 +124,12 @@ public class CustomerPictureService extends AbstractService {
|
||||
return customers.parallelStream().map(CustomerListValue::builder).toList();
|
||||
}
|
||||
|
||||
public CustomerValue get(Long id) {
|
||||
public CustomerValue get(Long id, String baseUrl) {
|
||||
Customer customer = entityManager.find(Customer.class, id);
|
||||
if (customer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CustomerValue.builder(customer);
|
||||
return CustomerValue.builder(customer, baseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.service;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.ejb.LocalBean;
|
||||
import jakarta.ejb.Stateless;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Picture;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.ImageUtil;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.StorageUtils.StorageState;
|
||||
|
||||
/**
|
||||
@@ -24,6 +28,9 @@ import marketing.heyday.hartmann.fotodocumentation.core.utils.StorageUtils.Stora
|
||||
@PermitAll
|
||||
public class PictureService extends AbstractService {
|
||||
private static final Log LOG = LogFactory.getLog(PictureService.class);
|
||||
|
||||
@Inject
|
||||
private ImageUtil imageUtil;
|
||||
|
||||
public StorageState delete(Long id) {
|
||||
return super.delete(Picture.class, id);
|
||||
@@ -41,4 +48,17 @@ public class PictureService extends AbstractService {
|
||||
return StorageState.NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getImage(Long id, int size) {
|
||||
try {
|
||||
Picture entity = entityManager.getReference(Picture.class, id);
|
||||
|
||||
String base64 = entity.getImage();
|
||||
return Base64.getDecoder().decode(base64);
|
||||
} catch (EntityNotFoundException e) {
|
||||
LOG.warn("Failed to get image for id " + id, e);
|
||||
ejbContext.setRollbackOnly();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
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.util.Base64;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 2 Feb 2026
|
||||
*/
|
||||
|
||||
public class ImageUtil {
|
||||
private static final Log LOG = LogFactory.getLog(ImageUtil.class);
|
||||
|
||||
private static final int NORMAL_MAX_WIDTH = 1200;
|
||||
private static final float NORMAL_QUALITY = 0.75f;
|
||||
|
||||
private static final int THUMBNAIL_MAX_WIDTH = 200;
|
||||
private static final float THUMBNAIL_QUALITY = 0.6f;
|
||||
|
||||
/**
|
||||
* size 1 is original
|
||||
* size 2 is normal (web)
|
||||
* size 3 is thumbnail
|
||||
*/
|
||||
public byte[] getImage(String base64, int size) {
|
||||
byte[] original = Base64.getDecoder().decode(base64);
|
||||
return switch (size) {
|
||||
case 1 -> original;
|
||||
case 2 -> normal(original);
|
||||
case 3 -> thumbnail(original);
|
||||
default -> original;
|
||||
};
|
||||
}
|
||||
|
||||
private byte[] normal(byte[] original) {
|
||||
return resize(original, NORMAL_MAX_WIDTH, NORMAL_QUALITY);
|
||||
}
|
||||
|
||||
private byte[] thumbnail(byte[] original) {
|
||||
return resize(original, THUMBNAIL_MAX_WIDTH, THUMBNAIL_QUALITY);
|
||||
}
|
||||
|
||||
private byte[] resize(byte[] original, int maxWidth, float quality) {
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(original));
|
||||
if (image == null) {
|
||||
LOG.error("Failed to read image from byte array");
|
||||
return original;
|
||||
}
|
||||
|
||||
int originalWidth = image.getWidth();
|
||||
int originalHeight = image.getHeight();
|
||||
|
||||
if (originalWidth <= maxWidth) {
|
||||
return original;
|
||||
}
|
||||
|
||||
double scale = (double) maxWidth / originalWidth;
|
||||
int targetWidth = maxWidth;
|
||||
int targetHeight = (int) (originalHeight * scale);
|
||||
|
||||
BufferedImage resized = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2d = resized.createGraphics();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2d.drawImage(image, 0, 0, targetWidth, targetHeight, null);
|
||||
g2d.dispose();
|
||||
|
||||
return writeJpeg(resized, quality);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to resize image", e);
|
||||
return original;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] writeJpeg(BufferedImage image, float quality) throws IOException {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
|
||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
param.setCompressionQuality(quality);
|
||||
|
||||
try (ImageOutputStream ios = ImageIO.createImageOutputStream(output)) {
|
||||
writer.setOutput(ios);
|
||||
writer.write(null, new IIOImage(image, null, null), param);
|
||||
} finally {
|
||||
writer.dispose();
|
||||
}
|
||||
return output.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import jakarta.ejb.EJB;
|
||||
import jakarta.enterprise.context.RequestScoped;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.service.CustomerPictureService;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerListValue;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerValue;
|
||||
@@ -32,6 +34,9 @@ import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerValue;
|
||||
@Path("customer")
|
||||
public class CustomerResource {
|
||||
private static final Log LOG = LogFactory.getLog(CustomerResource.class);
|
||||
|
||||
@Context
|
||||
private UriInfo uriInfo;
|
||||
|
||||
@EJB
|
||||
private CustomerPictureService customerPictureService;
|
||||
@@ -56,7 +61,9 @@ public class CustomerResource {
|
||||
@ApiResponse(responseCode = "200", description = "Successfully retrieved customer value", content = @Content(mediaType = JSON_OUT, array = @ArraySchema(schema = @Schema(implementation = CustomerValue.class))))
|
||||
public Response doGetDetailCustomer(@PathParam("id") Long id) {
|
||||
LOG.debug("Get Customer details for id " + id);
|
||||
var retVal = customerPictureService.get(id);
|
||||
String baseUrl = uriInfo.getBaseUri().toString();
|
||||
|
||||
var retVal = customerPictureService.get(id, baseUrl);
|
||||
return Response.ok().entity(retVal).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest;
|
||||
|
||||
import static marketing.heyday.hartmann.fotodocumentation.rest.jackson.ApplicationConfigApi.JSON_OUT;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jboss.resteasy.annotations.GZIP;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
@@ -11,6 +16,7 @@ import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.Response.ResponseBuilder;
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
import jakarta.ws.rs.core.StreamingOutput;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.service.PictureService;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.StorageUtils.StorageState;
|
||||
|
||||
@@ -27,10 +33,10 @@ import marketing.heyday.hartmann.fotodocumentation.core.utils.StorageUtils.Stora
|
||||
@Path("picture")
|
||||
public class PictureResource {
|
||||
private static final Log LOG = LogFactory.getLog(PictureResource.class);
|
||||
|
||||
|
||||
@EJB
|
||||
private PictureService pictureService;
|
||||
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Operation(summary = "Delete picture from database")
|
||||
@@ -42,21 +48,41 @@ public class PictureResource {
|
||||
var state = pictureService.delete(id);
|
||||
return deleteResponse(state).build();
|
||||
}
|
||||
|
||||
|
||||
@PUT
|
||||
@Path("evaluation/{id}")
|
||||
@Operation(summary = "Update evaluation for picture data to database")
|
||||
@ApiResponse(responseCode = "200", description = "Task successfully updated")
|
||||
public Response doUpdateEvaluation(@PathParam("id") Long id, @QueryParam("evaluation") Integer value) {
|
||||
if(value == null || value < 1 || value >3) {
|
||||
if (value == null || value < 1 || value > 3) {
|
||||
return Response.status(Status.BAD_REQUEST).build();
|
||||
}
|
||||
StorageState state = pictureService.updateEvaluationStatus(id, value);
|
||||
return deleteResponse(state).build();
|
||||
}
|
||||
|
||||
|
||||
@GZIP
|
||||
@GET
|
||||
@Path("image/{id}")
|
||||
@Produces(JSON_OUT)
|
||||
@Operation(summary = "Get customer value")
|
||||
@ApiResponse(responseCode = "200", description = "Successfully retrieved picture value")
|
||||
public Response doGetPictureImage(@PathParam("id") Long id, @QueryParam("size") int size) {
|
||||
LOG.debug("Get Picture for id " + id + " with size " + size);
|
||||
byte[] retVal = pictureService.getImage(id, size);
|
||||
if (retVal == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
StreamingOutput streamingOutput = (OutputStream output) -> {
|
||||
LOG.debug("Start writing content to OutputStream available bytes");
|
||||
output.write(retVal);
|
||||
};
|
||||
return Response.status(Status.OK).entity(streamingOutput).build();
|
||||
}
|
||||
|
||||
protected ResponseBuilder deleteResponse(StorageState state) {
|
||||
return switch(state) {
|
||||
return switch (state) {
|
||||
case OK -> Response.status(Status.OK);
|
||||
case NOT_FOUND -> Response.status(Status.NOT_FOUND);
|
||||
default -> Response.status(Status.INTERNAL_SERVER_ERROR);
|
||||
|
||||
@@ -17,11 +17,11 @@ import marketing.heyday.hartmann.fotodocumentation.core.model.Customer;
|
||||
@Schema(name = "Customer")
|
||||
public record CustomerValue(Long id, String name, String customerNumber, String city, String zip, List<PictureValue> pictures) {
|
||||
|
||||
public static CustomerValue builder(Customer customer) {
|
||||
public static CustomerValue builder(Customer customer, String baseUrl) {
|
||||
if (customer == null) {
|
||||
return null;
|
||||
}
|
||||
List<PictureValue> pictures = customer.getPictures().parallelStream().map(PictureValue::builder).filter(p -> p != null).toList();
|
||||
List<PictureValue> pictures = customer.getPictures().parallelStream().map(p -> PictureValue.builder(p, baseUrl)).filter(p -> p != null).toList();
|
||||
return new CustomerValue(customer.getCustomerId(), customer.getName(), customer.getCustomerNumber(), customer.getCity(), customer.getZip(), pictures);
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,15 @@ import marketing.heyday.hartmann.fotodocumentation.core.model.Picture;
|
||||
*/
|
||||
|
||||
@Schema(name = "Picture")
|
||||
public record PictureValue(Long id, String comment, String category, String image, Date pictureDate, String username, Integer evaluation) {
|
||||
public record PictureValue(Long id, String comment, String category, Date pictureDate, String username, Integer evaluation, String imageUrl, String normalSizeUrl, String thumbnailSizeUrl) {
|
||||
|
||||
public static PictureValue builder(Picture picture) {
|
||||
public static PictureValue builder(Picture picture, String baseUrl) {
|
||||
if (picture == null) {
|
||||
return null;
|
||||
}
|
||||
return new PictureValue(picture.getPictureId(), picture.getComment(), picture.getCategory(), picture.getImage(), picture.getPictureDate(), picture.getUsername(), picture.getEvaluation());
|
||||
String imageUrl = baseUrl + "picture/image/" + picture.getPictureId() + "?size=1";
|
||||
String normalSizeUrl = baseUrl + "picture/image/" + picture.getPictureId() + "?size=2";
|
||||
String thumbnailSizeUrl = baseUrl + "picture/image/" + picture.getPictureId() + "?size=3";
|
||||
return new PictureValue(picture.getPictureId(), picture.getComment(), picture.getCategory(), picture.getPictureDate(), picture.getUsername(), picture.getEvaluation(), imageUrl, normalSizeUrl, thumbnailSizeUrl);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user