parent
3083b2596c
commit
11f77cf995
8 changed files with 331 additions and 11 deletions
@ -0,0 +1,281 @@ |
|||||||
|
/** |
||||||
|
* 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.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 = 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 imageFile Info about uploaded data. |
||||||
|
* |
||||||
|
* CalledFrom @see |
||||||
|
* RecipeDetailBean#ajaxUploadImage(AjaxBehaviorEvent |
||||||
|
* event) |
||||||
|
*/ |
||||||
|
public static void importImage(Recipe recipe, |
||||||
|
Part imageFile) { |
||||||
|
// imageFile.getContentType(); // ex: image/jpeg
|
||||||
|
try { |
||||||
|
byte[] bytes = null; |
||||||
|
|
||||||
|
InputStream istream = imageFile.getInputStream(); |
||||||
|
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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
/** |
||||||
|
* Copyright (C) 2021, Tim Holloway |
||||||
|
* |
||||||
|
* Date written: Dec 28, 2021 |
||||||
|
* Author: Tim Holloway <timh@mousetech.com> |
||||||
|
*/ |
||||||
|
/** |
||||||
|
* @author timh |
||||||
|
* @since Dec 28, 2021 |
||||||
|
*/ |
||||||
|
package com.mousetech.gourmetj.springweb; |
@ -0,0 +1,4 @@ |
|||||||
|
.redBox { |
||||||
|
color: red; |
||||||
|
background-color: blue; |
||||||
|
} |
Loading…
Reference in new issue