287 lines
7.4 KiB
Java
287 lines
7.4 KiB
Java
/**
|
|
* Copyright (C) 2021, Tim Holloway
|
|
*
|
|
* Date written: Nov 26, 2021
|
|
* Author: Tim Holloway <timh@mousetech.com>
|
|
*/
|
|
package com.mousetech.gourmetj.springweb;
|
|
|
|
import java.awt.Color;
|
|
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.InputStream;
|
|
|
|
import javax.imageio.ImageIO;
|
|
import javax.imageio.stream.ImageInputStream;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpSession;
|
|
import javax.servlet.http.Part;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.http.HttpHeaders;
|
|
import org.springframework.http.MediaType;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.stereotype.Controller;
|
|
import org.springframework.web.bind.annotation.GetMapping;
|
|
import org.springframework.web.bind.annotation.PathVariable;
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
|
import com.mousetech.gourmetj.UserSession;
|
|
import com.mousetech.gourmetj.persistence.model.Recipe;
|
|
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
|
|
|
/**
|
|
* @author timh
|
|
* @since Nov 26, 2021
|
|
*/
|
|
@Controller
|
|
@RequestMapping(path="/img")
|
|
public class PictureController {
|
|
|
|
/* Logger */
|
|
private static final Logger log =
|
|
LoggerFactory.getLogger(PictureController.class);
|
|
|
|
@Autowired
|
|
private RecipeService recipeService;
|
|
|
|
/**
|
|
* @return the recipeService
|
|
*/
|
|
public RecipeService getRecipeService() {
|
|
return recipeService;
|
|
}
|
|
|
|
/**
|
|
* @param recipeService the recipeService to set
|
|
*/
|
|
public void setRecipeService(RecipeService recipeService) {
|
|
this.recipeService = recipeService;
|
|
}
|
|
|
|
// ***
|
|
/**
|
|
* Default image returned if a recipe has no actual image.
|
|
* It's thumbnail-sized, so does double duty.
|
|
*/
|
|
static byte[] BLANK_IMAGE;
|
|
|
|
public PictureController() {
|
|
init();
|
|
}
|
|
|
|
/**
|
|
* General setup. Constructs BLANK_IMAGE.
|
|
*/
|
|
private void init() {
|
|
BufferedImage tbi = new BufferedImage(40, 40,
|
|
BufferedImage.TYPE_INT_ARGB);
|
|
Graphics2D ig2 = tbi.createGraphics();
|
|
ig2.setBackground(Color.WHITE);
|
|
ig2.clearRect(0, 0, 40, 40);
|
|
|
|
try {
|
|
ByteArrayOutputStream os =
|
|
new ByteArrayOutputStream(946);
|
|
ImageIO.write(tbi, "png", os);
|
|
BLANK_IMAGE = os.toByteArray();
|
|
} catch (IOException e) {
|
|
log.error("Unable to create BLANK_IMAGE", e);
|
|
}
|
|
}
|
|
|
|
// ==========================================
|
|
@GetMapping(value = "/picture/{recipeId}")
|
|
@PostMapping(value = "/picture/{recipeId}")
|
|
public ResponseEntity<byte[]> bigPicture(
|
|
@PathVariable long recipeId) {
|
|
|
|
Recipe recipe = recipeService.findByPrimaryKey(recipeId);
|
|
if (recipe != null) {
|
|
HttpHeaders httpHeaders = new HttpHeaders();
|
|
httpHeaders.set(HttpHeaders.CONTENT_TYPE,
|
|
MediaType.IMAGE_PNG_VALUE);
|
|
httpHeaders.set(HttpHeaders.CACHE_CONTROL,
|
|
"no-cache");
|
|
byte[] image = recipe.getImage();
|
|
if (image == null) {
|
|
image = BLANK_IMAGE;
|
|
}
|
|
return ResponseEntity.ok().headers(httpHeaders)
|
|
.body(image);
|
|
}
|
|
|
|
// NotFound, need dummy picture!:
|
|
return ResponseEntity.status(404).body(new byte[0]);
|
|
}
|
|
|
|
/**
|
|
* Dummy picture request. Made when recipeId is not yet
|
|
* assigned.
|
|
*
|
|
* @param req The HttpServletRequest from Spring.
|
|
* @return Image response.
|
|
*/
|
|
@GetMapping(value = "/picture/")
|
|
public ResponseEntity<byte[]> bigPicture(
|
|
HttpServletRequest req) {
|
|
|
|
final HttpSession httpSession = req.getSession(false);
|
|
final UserSession userSession = (UserSession) httpSession
|
|
.getAttribute("userSession");
|
|
|
|
Recipe editRecipe = userSession.getRecipe();
|
|
|
|
HttpHeaders httpHeaders = new HttpHeaders();
|
|
httpHeaders.set(HttpHeaders.CONTENT_TYPE,
|
|
MediaType.IMAGE_PNG_VALUE);
|
|
httpHeaders.set(HttpHeaders.CACHE_CONTROL, "no-cache");
|
|
byte[] image = null;
|
|
if ( editRecipe != null ) {
|
|
// Otherwise we are creating new recipe, pre-display
|
|
image = editRecipe.getImage();
|
|
}
|
|
|
|
if (image == null) {
|
|
image = BLANK_IMAGE;
|
|
}
|
|
return ResponseEntity.ok().headers(httpHeaders)
|
|
.body(image);
|
|
}
|
|
|
|
@GetMapping(value = "/thumb/{recipeId}")
|
|
public ResponseEntity<byte[]> thumbnail(
|
|
@PathVariable long recipeId) {
|
|
|
|
Recipe recipe = recipeService.findByPrimaryKey(recipeId);
|
|
if (recipe != null) {
|
|
HttpHeaders httpHeaders = new HttpHeaders();
|
|
httpHeaders.set(HttpHeaders.CONTENT_TYPE,
|
|
MediaType.IMAGE_PNG_VALUE);
|
|
byte[] image = recipe.getThumb();
|
|
// Empty thumbs have been seen with "H"!
|
|
if (image == null || image.length < 2) {
|
|
image = BLANK_IMAGE;
|
|
}
|
|
return ResponseEntity.ok().headers(httpHeaders)
|
|
.body(image);
|
|
}
|
|
|
|
// NotFound, need dummy picture!:
|
|
return ResponseEntity.status(404).body(new byte[0]);
|
|
}
|
|
|
|
/**
|
|
* Take imageFile input and store it as image for recipe.
|
|
* Generate thumnail
|
|
*
|
|
* @param recipe Recipe to store into.
|
|
* @param bs Info about uploaded data.
|
|
*
|
|
* CalledFrom @see
|
|
* RecipeDetailBean#ajaxUploadImage(AjaxBehaviorEvent
|
|
* event)
|
|
*/
|
|
public static void importImage(Recipe recipe,
|
|
byte[] bs) {
|
|
try {
|
|
byte[] bytes = null;
|
|
|
|
InputStream istream =new ByteArrayInputStream(bs);
|
|
ImageInputStream stream =
|
|
ImageIO.createImageInputStream(istream);
|
|
BufferedImage bi = ImageIO.read(stream);
|
|
|
|
if ((bi.getWidth() > 512) || (bi.getWidth() > 512)) {
|
|
bi = scaleImageSanely(bi);
|
|
}
|
|
|
|
ByteArrayOutputStream baos =
|
|
new ByteArrayOutputStream();
|
|
ImageIO.write(bi, "png", baos);
|
|
bytes = baos.toByteArray();
|
|
recipe.setImage(bytes);
|
|
|
|
final int thumbHeight = 40;
|
|
float ratio = ((float) bi.getHeight()) / thumbHeight;
|
|
int thumbWidth = (int) (bi.getWidth() / ratio);
|
|
|
|
BufferedImage resizedImg =
|
|
new BufferedImage(thumbWidth, thumbHeight,
|
|
BufferedImage.TRANSLUCENT);
|
|
|
|
// Create a device-independent object to draw the
|
|
// resized image
|
|
Graphics2D g2 = resizedImg.createGraphics();
|
|
|
|
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
|
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
|
|
|
// Finally draw the source image in the Graphics2D
|
|
// with the desired size.
|
|
g2.drawImage(bi, 0, 0, thumbWidth, thumbHeight,
|
|
null);
|
|
|
|
// Disposes of this graphics context and releases any
|
|
// system resources that it is using
|
|
g2.dispose();
|
|
|
|
baos = new ByteArrayOutputStream();
|
|
ImageIO.write(resizedImg, "png", baos);
|
|
bytes = baos.toByteArray();
|
|
recipe.setThumb(bytes);
|
|
} catch (IOException e) {
|
|
// TODO Auto-generated catch block
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reduce image so that width or height fit.
|
|
*
|
|
* @param bi
|
|
*
|
|
* @return
|
|
*/
|
|
private static BufferedImage scaleImageSanely(
|
|
BufferedImage bi) {
|
|
int w0 = bi.getWidth();
|
|
int h0 = bi.getHeight();
|
|
|
|
int w1 = w0;
|
|
int h1 = h0;
|
|
|
|
double ratio = 1.0;
|
|
|
|
if (h0 > 512) {
|
|
h1 = 512;
|
|
ratio = ((double) h0) / ((double) h1);
|
|
w1 = (int) (w0 / ratio);
|
|
}
|
|
if (w1 > 512) {
|
|
w0 = w1;
|
|
w1 = 512;
|
|
ratio = ((double) w0) / ((double) w1);
|
|
w1 = (int) (w0 / ratio);
|
|
}
|
|
|
|
BufferedImage resizedImg = new BufferedImage(w1, h1,
|
|
BufferedImage.TRANSLUCENT);
|
|
|
|
Graphics2D g2 = resizedImg.createGraphics();
|
|
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
|
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
|
g2.drawImage(bi, 0, 0, w1, h1, null);
|
|
g2.dispose();
|
|
|
|
return resizedImg;
|
|
}
|
|
}
|