From 37e6d26e02d51a4de257200bbf207712fa3a6980 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Sat, 23 Oct 2021 05:37:09 +0300 Subject: ImagesService refactoring * Merge ImagesService and ImageUtils * add avatar metadata to RSS feeds --- src/main/java/com/juick/service/ImagesService.java | 14 ++ .../java/com/juick/service/ImagesServiceImpl.java | 187 +++++++++++++++++++-- 2 files changed, 186 insertions(+), 15 deletions(-) (limited to 'src/main/java/com/juick/service') diff --git a/src/main/java/com/juick/service/ImagesService.java b/src/main/java/com/juick/service/ImagesService.java index 3fb06c63..0891dca9 100644 --- a/src/main/java/com/juick/service/ImagesService.java +++ b/src/main/java/com/juick/service/ImagesService.java @@ -17,11 +17,14 @@ package com.juick.service; +import com.juick.model.Attachment; import com.juick.model.Message; +import java.io.FileNotFoundException; import java.io.IOException; public interface ImagesService { + void setAttachmentMetadata(String baseUrl, Message msg) throws Exception; /** * Move attached image from temp folder to image folder. @@ -38,4 +41,15 @@ public interface ImagesService { * @param uid User id that is used to build image file names. */ void saveAvatar(String tempFilename, int uid) throws IOException; + + String getTemporaryDirectory(); + + String getImageDirectory(); + + /** + * Get image metadata + * @param resource URL + * @return image metadata + */ + Attachment getImageMetadata(String resourceUrl) throws FileNotFoundException, IOException; } diff --git a/src/main/java/com/juick/service/ImagesServiceImpl.java b/src/main/java/com/juick/service/ImagesServiceImpl.java index 72d5b97c..6687a59b 100644 --- a/src/main/java/com/juick/service/ImagesServiceImpl.java +++ b/src/main/java/com/juick/service/ImagesServiceImpl.java @@ -20,29 +20,58 @@ package com.juick.service; import com.juick.model.Attachment; import com.juick.model.Message; import com.juick.model.Photo; -import com.juick.util.ImageUtils; +import org.apache.commons.imaging.ImageReadException; +import org.apache.commons.imaging.Imaging; +import org.apache.commons.imaging.common.ImageMetadata; +import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; +import org.apache.commons.imaging.formats.tiff.TiffField; +import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.imgscalr.Scalr; +import org.imgscalr.Scalr.Rotation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; +import java.awt.image.BufferedImage; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Iterator; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; public class ImagesServiceImpl implements ImagesService { - private final ImageUtils imageUtils; + + private static final Logger logger = LoggerFactory.getLogger(ImagesService.class); + private final String imgDir; + private final String tmpDir; public ImagesServiceImpl(String imgDir, String tmpDir) { this.imgDir = imgDir; - imageUtils = new ImageUtils(imgDir, tmpDir); + this.tmpDir = tmpDir; } + @Override public void setAttachmentMetadata(String baseUrl, Message msg) throws Exception { if (!StringUtils.isEmpty(msg.getAttachmentType())) { Photo photo = new Photo(); - if (msg.getRid()> 0) { - photo.setSmall(String.format("%sphotos-512/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), msg.getAttachmentType())); - photo.setMedium(String.format("%sphotos-1024/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), msg.getAttachmentType())); - photo.setThumbnail(String.format("%sps/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), msg.getAttachmentType())); + if (msg.getRid() > 0) { + photo.setSmall(String.format("%sphotos-512/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), + msg.getAttachmentType())); + photo.setMedium(String.format("%sphotos-1024/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), + msg.getAttachmentType())); + photo.setThumbnail( + String.format("%sps/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), msg.getAttachmentType())); } else { photo.setSmall(String.format("%sphotos-512/%d.%s", baseUrl, msg.getMid(), msg.getAttachmentType())); photo.setMedium(String.format("%sphotos-1024/%d.%s", baseUrl, msg.getMid(), msg.getAttachmentType())); @@ -65,20 +94,20 @@ public class ImagesServiceImpl implements ImagesService { builder.append("-").append(msg.getRid()); } builder.append(".").append(msg.getAttachmentType()); - String originalUrl = builder.toString(); + String originalUrl = builder.toString(); - Attachment original = imageUtils.getAttachment(fullImage); + Attachment original = getAttachment(fullImage); original.setUrl(originalUrl); - Attachment medium = imageUtils.getAttachment(mediumImage); + Attachment medium = getAttachment(mediumImage); medium.setUrl(photo.getMedium()); original.setMedium(medium); - Attachment small = imageUtils.getAttachment(smallImage); + Attachment small = getAttachment(smallImage); small.setUrl(photo.getSmall()); original.setSmall(small); - Attachment thumb = imageUtils.getAttachment(thumbnailImage); + Attachment thumb = getAttachment(thumbnailImage); thumb.setUrl(photo.getMedium()); original.setThumbnail(thumb); @@ -87,12 +116,140 @@ public class ImagesServiceImpl implements ImagesService { } @Override - public void saveImageWithPreviews(String tempFilename, String outputFilename) throws IOException { - imageUtils.saveImageWithPreviews(tempFilename, outputFilename); + public Attachment getImageMetadata(String resourceLocation) throws FileNotFoundException, IOException { + return getAttachment(ResourceUtils.getFile(resourceLocation)); + } + + /** + * Returns BufferedImage, same as ImageIO.read() does. + * + *

+ * JPEG images with EXIF metadata are rotated according to Orientation tag. + * + * @param imageFile a File to read from. + */ + private static BufferedImage readImageWithOrientation(File imageFile) throws IOException { + + BufferedImage image = ImageIO.read(imageFile); + if (!FilenameUtils.getExtension(imageFile.getName()).equals("jpg")) { + return image; + } + + try { + ImageMetadata metadata = Imaging.getMetadata(imageFile); + + if (metadata instanceof JpegImageMetadata) { + JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; + TiffField orientationField = jpegMetadata.findEXIFValue(TiffTagConstants.TIFF_TAG_ORIENTATION); + + if (orientationField != null) { + int orientation = orientationField.getIntValue(); + switch (orientation) { + case TiffTagConstants.ORIENTATION_VALUE_ROTATE_90_CW: + image = Scalr.rotate(image, Rotation.CW_90); + break; + case TiffTagConstants.ORIENTATION_VALUE_ROTATE_180: + image = Scalr.rotate(image, Rotation.CW_180); + break; + case TiffTagConstants.ORIENTATION_VALUE_ROTATE_270_CW: + image = Scalr.rotate(image, Rotation.CW_270); + break; + case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL: + image = Scalr.rotate(image, Rotation.FLIP_HORZ); + break; + case TiffTagConstants.ORIENTATION_VALUE_MIRROR_VERTICAL: + image = Scalr.rotate(image, Rotation.FLIP_VERT); + break; + case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_90_CW: + image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_90); + break; + case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_270_CW: + image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_270); + break; + case TiffTagConstants.ORIENTATION_VALUE_HORIZONTAL_NORMAL: + default: + // do nothing + break; + } + } + } + } catch (ImageReadException | IOException e) { + // failed to read metadata. + // nothing to do here, return image as is. + } + + return image; } @Override + public void saveImageWithPreviews(String tempFilename, String outputFilename) throws IOException { + String ext = FilenameUtils.getExtension(outputFilename); + + Path outputImagePath = Paths.get(imgDir, "p", outputFilename); + // this throws strange exceptions + // Files.move(Paths.get(tmpDir, tempFilename), outputImagePath); + FileUtils.moveFile(Paths.get(tmpDir, tempFilename).toFile(), outputImagePath.toFile()); + BufferedImage originalImage = readImageWithOrientation(outputImagePath.toFile()); + + int width = originalImage.getWidth(); + int height = originalImage.getHeight(); + int maxDimension = Math.max(width, height); + BufferedImage image1024 = (maxDimension > 1024) ? Scalr.resize(originalImage, 1024) : originalImage; + BufferedImage image0512 = (maxDimension > 512) ? Scalr.resize(originalImage, 512) : originalImage; + BufferedImage image0160 = (maxDimension > 160) ? Scalr.resize(originalImage, 160) : originalImage; + ImageIO.write(image1024, ext, Paths.get(imgDir, "photos-1024", outputFilename).toFile()); + ImageIO.write(image0512, ext, Paths.get(imgDir, "photos-512", outputFilename).toFile()); + ImageIO.write(image0160, ext, Paths.get(imgDir, "ps", outputFilename).toFile()); + } + public void saveAvatar(String tempFilename, int uid) throws IOException { - imageUtils.saveAvatar(tempFilename, uid); + String ext = FilenameUtils.getExtension(tempFilename); + String originalName = String.format("%s.%s", uid, ext); + Path originalPath = Paths.get(imgDir, "ao", originalName); + Files.move(Paths.get(tmpDir, tempFilename), originalPath, StandardCopyOption.REPLACE_EXISTING); + BufferedImage originalImage = ImageIO.read(originalPath.toFile()); + + String targetExt = "png"; + String targetName = String.format("%s.%s", uid, targetExt); + ImageIO.write(Scalr.resize(originalImage, 96), targetExt, Paths.get(imgDir, "a", targetName).toFile()); + ImageIO.write(Scalr.resize(originalImage, 32), targetExt, Paths.get(imgDir, "as", targetName).toFile()); + } + + public Attachment getAttachment(File imgFile) throws IOException { + Attachment attachment = new Attachment(); + if (imgFile.exists()) { + try (ImageInputStream stream = ImageIO.createImageInputStream(imgFile)) { + Iterator iter = ImageIO.getImageReaders(stream); + while (iter.hasNext()) { + ImageReader reader = iter.next(); + try { + reader.setInput(stream); + attachment.setWidth(reader.getWidth(reader.getMinIndex())); + attachment.setHeight(reader.getHeight(reader.getMinIndex())); + return attachment; + } catch (Exception e) { + logger.debug("Error reading {}, trying next reader", imgFile.getAbsolutePath()); + } finally { + reader.dispose(); + } + } + } + } else { + logger.warn("File not exists yet: {}", imgFile.getAbsolutePath()); + return attachment; + } + + logger.warn("Not a known image file {}", imgFile.getAbsolutePath()); + return attachment; + } + + @Override + public String getImageDirectory() { + return imgDir; + } + + @Override + public String getTemporaryDirectory() { + return tmpDir; } } -- cgit v1.2.3