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