From 4b96a9b2e71b7a67effdd55b26ca532ff849d0ef Mon Sep 17 00:00:00 2001
From: Vitaly Takmazov
Date: Sat, 23 Oct 2021 08:09:05 +0300
Subject: ImagesService -> StorageService
img_path -> storage_path property
---
src/main/java/com/juick/CommandsManager.java | 67 ++---
src/main/java/com/juick/TelegramBotManager.java | 7 +-
src/main/java/com/juick/XMPPManager.java | 7 +-
src/main/java/com/juick/config/StorageConfig.java | 37 ---
src/main/java/com/juick/config/WebConfig.java | 14 +-
.../juick/service/FileSystemStorageService.java | 276 +++++++++++++++++++++
src/main/java/com/juick/service/ImagesService.java | 55 ----
.../java/com/juick/service/ImagesServiceImpl.java | 255 -------------------
.../com/juick/service/MessagesServiceImpl.java | 6 +-
.../java/com/juick/service/StorageService.java | 55 ++++
src/main/java/com/juick/util/ImageUtils.java | 48 ----
src/main/java/com/juick/www/WebApp.java | 5 +
src/main/java/com/juick/www/api/Post.java | 52 ++--
src/main/java/com/juick/www/api/Service.java | 7 +-
src/main/java/com/juick/www/api/Users.java | 8 +-
.../java/com/juick/www/controllers/Settings.java | 8 +-
src/main/java/com/juick/www/rss/MessagesView.java | 43 ++--
.../java/com/juick/server/tests/ServerTests.java | 30 ++-
18 files changed, 475 insertions(+), 505 deletions(-)
delete mode 100644 src/main/java/com/juick/config/StorageConfig.java
create mode 100644 src/main/java/com/juick/service/FileSystemStorageService.java
delete mode 100644 src/main/java/com/juick/service/ImagesService.java
delete mode 100644 src/main/java/com/juick/service/ImagesServiceImpl.java
create mode 100644 src/main/java/com/juick/service/StorageService.java
delete mode 100644 src/main/java/com/juick/util/ImageUtils.java
(limited to 'src')
diff --git a/src/main/java/com/juick/CommandsManager.java b/src/main/java/com/juick/CommandsManager.java
index e2a50444..6d315a7d 100644
--- a/src/main/java/com/juick/CommandsManager.java
+++ b/src/main/java/com/juick/CommandsManager.java
@@ -17,20 +17,44 @@
package com.juick;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.inject.Inject;
+
+import com.juick.model.CommandResult;
import com.juick.model.Message;
import com.juick.model.Tag;
-import com.juick.model.User;
-import com.juick.util.formatters.PlainTextFormatter;
-import com.juick.model.CommandResult;
import com.juick.model.TagStats;
-import com.juick.www.api.SystemActivity;
-import com.juick.util.annotation.UserCommand;
-import com.juick.util.HttpUtils;
-import com.juick.www.WebApp;
-import com.juick.service.*;
+import com.juick.model.User;
+import com.juick.service.MessagesService;
+import com.juick.service.PMQueriesService;
+import com.juick.service.PrivacyQueriesService;
+import com.juick.service.ShowQueriesService;
+import com.juick.service.StorageService;
+import com.juick.service.SubscriptionService;
+import com.juick.service.TagService;
+import com.juick.service.UserService;
import com.juick.service.activities.DeleteMessageEvent;
-import com.juick.service.component.*;
+import com.juick.service.component.PingEvent;
+import com.juick.service.component.SystemEvent;
+import com.juick.util.HttpUtils;
import com.juick.util.MessageUtils;
+import com.juick.util.annotation.UserCommand;
+import com.juick.util.formatters.PlainTextFormatter;
+import com.juick.www.WebApp;
+import com.juick.www.api.SystemActivity;
+
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
@@ -39,19 +63,8 @@ import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.text.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.net.URI;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
/**
*
* @author ugnich
@@ -72,12 +85,10 @@ public class CommandsManager {
private PrivacyQueriesService privacyQueriesService;
@Inject
private SubscriptionService subscriptionService;
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
@Inject
- private ApplicationEventPublisher applicationEventPublisher;
+ private StorageService storageService;
@Inject
- private ImagesService imagesService;
+ private ApplicationEventPublisher applicationEventPublisher;
@Inject
private WebApp webApp;
@Inject
@@ -125,7 +136,7 @@ public class CommandsManager {
String attachmentType = null;
if (haveAttachment) {
attachmentFName = attachment.getScheme().equals("juick") ? attachment.getHost()
- : HttpUtils.downloadImage(attachment.toURL(), tmpDir).getHost();
+ : HttpUtils.downloadImage(attachment.toURL(), storageService.getTemporaryDirectory()).getHost();
attachmentType = attachmentFName.substring(attachmentFName.length() - 3);
}
if (StringUtils.isEmpty(body) && !haveAttachment) {
@@ -134,7 +145,7 @@ public class CommandsManager {
int mid = messagesService.createMessage(user.getUid(), body, attachmentType, tags.getRight());
if (haveAttachment) {
String fname = String.format("%d.%s", mid, attachmentType);
- imagesService.saveImageWithPreviews(attachmentFName, fname);
+ storageService.saveImageWithPreviews(attachmentFName, fname);
}
Message msg = messagesService.getMessage(mid).orElseThrow(IllegalStateException::new);
msg.getUser().setAvatar(webApp.getAvatarUrl(msg.getUser()));
@@ -548,7 +559,7 @@ public class CommandsManager {
attachmentType = attachmentFName.substring(attachmentFName.length() - 3);
} else {
try {
- attachmentFName = HttpUtils.downloadImage(attachment.toURL(), tmpDir).getHost();
+ attachmentFName = HttpUtils.downloadImage(attachment.toURL(), storageService.getTemporaryDirectory()).getHost();
attachmentType = attachmentFName.substring(attachmentFName.length() - 3);
} catch (IOException e) {
logger.warn("Can not download {}", attachment.toURL());
@@ -561,7 +572,7 @@ public class CommandsManager {
if (newrid > 0) {
if (haveAttachment && attachmentProcessed) {
String fname = String.format("%d-%d.%s", mid, newrid, attachmentType);
- imagesService.saveImageWithPreviews(attachmentFName, fname);
+ storageService.saveImageWithPreviews(attachmentFName, fname);
}
applicationEventPublisher.publishEvent(
new SystemEvent(this, SystemActivity.read(user, msg.get())));
diff --git a/src/main/java/com/juick/TelegramBotManager.java b/src/main/java/com/juick/TelegramBotManager.java
index 33cac896..abed77fc 100644
--- a/src/main/java/com/juick/TelegramBotManager.java
+++ b/src/main/java/com/juick/TelegramBotManager.java
@@ -23,6 +23,7 @@ import com.juick.model.CommandResult;
import com.juick.www.api.SystemActivity;
import com.juick.util.HttpUtils;
import com.juick.service.MessagesService;
+import com.juick.service.StorageService;
import com.juick.service.TelegramService;
import com.juick.service.UserService;
import com.juick.service.component.SystemEvent;
@@ -91,8 +92,8 @@ public class TelegramBotManager implements NotificationListener {
private CommandsManager commandsManager;
@Inject
private ApplicationEventPublisher applicationEventPublisher;
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
+ @Inject
+ private StorageService storageService;
@Inject
private User serviceUser;
@@ -170,7 +171,7 @@ public class TelegramBotManager implements NotificationListener {
logger.info("got file {}", response.file());
try {
URL fileURL = new URL(bot.getFullFilePath(response.file()));
- attachment = HttpUtils.downloadImage(fileURL, tmpDir);
+ attachment = HttpUtils.downloadImage(fileURL, storageService.getTemporaryDirectory());
} catch (Exception e) {
logger.warn("attachment exception", e);
}
diff --git a/src/main/java/com/juick/XMPPManager.java b/src/main/java/com/juick/XMPPManager.java
index b42b4b9f..be1f8fd6 100644
--- a/src/main/java/com/juick/XMPPManager.java
+++ b/src/main/java/com/juick/XMPPManager.java
@@ -25,6 +25,7 @@ import com.juick.www.WebApp;
import com.juick.util.xmpp.iq.MessageQuery;
import com.juick.service.MessagesService;
import com.juick.service.PMQueriesService;
+import com.juick.service.StorageService;
import com.juick.service.UserService;
import com.juick.service.component.*;
import com.juick.util.MessageUtils;
@@ -102,8 +103,8 @@ public class XMPPManager implements NotificationListener {
private String componentHost;
@Value("${xmpp_password:secret}")
private String password;
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
+ @Inject
+ private StorageService storageService;
@Value("classpath:juick.png")
private Resource vCardImage;
@@ -199,7 +200,7 @@ public class XMPPManager implements NotificationListener {
DigestUtils.md5Hex(String.format("%s-%s",
e.getInitiator().toString(), e.getSessionId()).getBytes()), attachmentExtension);
if (allowedTypes.contains(attachmentExtension)) {
- Path filePath = Paths.get(tmpDir, targetFilename);
+ Path filePath = Paths.get(storageService.getTemporaryDirectory(), targetFilename);
FileTransfer ft = e.accept(filePath).get();
ft.addFileTransferStatusListener(st -> {
logger.debug("{}: received {} of {}", e.getName(), st.getBytesTransferred(), e.getSize());
diff --git a/src/main/java/com/juick/config/StorageConfig.java b/src/main/java/com/juick/config/StorageConfig.java
deleted file mode 100644
index d105ec72..00000000
--- a/src/main/java/com/juick/config/StorageConfig.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2008-2020, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.config;
-
-import com.juick.service.ImagesService;
-import com.juick.service.ImagesServiceImpl;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class StorageConfig {
-
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
- @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String imgDir;
- @Bean
- public ImagesService imagesService() {
- return new ImagesServiceImpl(imgDir, tmpDir);
- }
-}
diff --git a/src/main/java/com/juick/config/WebConfig.java b/src/main/java/com/juick/config/WebConfig.java
index fcc61b72..94f30bd5 100644
--- a/src/main/java/com/juick/config/WebConfig.java
+++ b/src/main/java/com/juick/config/WebConfig.java
@@ -20,6 +20,8 @@ package com.juick.config;
import com.juick.*;
import com.juick.model.User;
import com.juick.service.HelpService;
+import com.juick.service.StorageService;
+import com.juick.service.FileSystemStorageService;
import com.juick.service.UserService;
import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.extension.FormatterExtension;
@@ -64,8 +66,14 @@ import javax.inject.Inject;
@EnableAsync(proxyTargetClass = true)
@EnableScheduling
public class WebConfig implements WebMvcConfigurer {
- @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String imgDir;
+ @Value("${upload_tmp_dir:#{systemProperties['java.io.tmpdir']}}")
+ private String tmpDir;
+ @Value("${storage_path:#{systemProperties['java.io.tmpdir']}}")
+ private String baseDir;
+ @Bean
+ public StorageService storageService() {
+ return new FileSystemStorageService(baseDir, tmpDir);
+ }
@Bean
public CaffeineCacheManager cacheManager() {
return new CaffeineCacheManager("help");
@@ -172,7 +180,7 @@ public class WebConfig implements WebMvcConfigurer {
try {
registry
.addResourceHandler("/**", "/i/a/**")
- .addResourceLocations("classpath:/static/", Paths.get(imgDir, "/a/").toUri().toURL().toString())
+ .addResourceLocations("classpath:/static/", Paths.get(baseDir, "/i/a/").toUri().toURL().toString())
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(false)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**", "/i/a/**"));
diff --git a/src/main/java/com/juick/service/FileSystemStorageService.java b/src/main/java/com/juick/service/FileSystemStorageService.java
new file mode 100644
index 00000000..021e5a9f
--- /dev/null
+++ b/src/main/java/com/juick/service/FileSystemStorageService.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2008-2020, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.service;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+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;
+
+import com.juick.model.Attachment;
+import com.juick.model.Message;
+import com.juick.model.Photo;
+import com.juick.model.User;
+
+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.StringUtils;
+
+public class FileSystemStorageService implements StorageService {
+
+ private static final Logger logger = LoggerFactory.getLogger(StorageService.class);
+
+ private final String baseDir;
+ private final String imgDir;
+ private final String tmpDir;
+ private final String avatarDir, avatarSmallDir, avatarOriginalDir;
+ private final String fullImageDir, mediumImageDir, smallImageDir, thumbnailImageDir;
+
+ public FileSystemStorageService(String baseDir, String tmpDir) {
+ this.baseDir = baseDir;
+ this.imgDir = Paths.get(baseDir, "i").toString();
+ this.tmpDir = tmpDir;
+ this.avatarDir = Paths.get(imgDir, "a").toString();
+ this.avatarOriginalDir = Paths.get(imgDir, "ao").toString();
+ this.avatarSmallDir = Paths.get(imgDir, "as").toString();
+ this.fullImageDir = Paths.get(imgDir, "p").toString();
+ this.mediumImageDir = Paths.get(imgDir, "photos-1024").toString();
+ this.smallImageDir = Paths.get(imgDir, "photos-512").toString();
+ this.thumbnailImageDir = Paths.get(imgDir, "ps").toString();
+ }
+
+ @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()));
+ } 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()));
+ photo.setThumbnail(String.format("%sps/%d.%s", baseUrl, msg.getMid(), msg.getAttachmentType()));
+ }
+ msg.setPhoto(photo);
+ String imageName = String.format("%s.%s", msg.getMid(), msg.getAttachmentType());
+ if (msg.getRid() > 0) {
+ imageName = String.format("%s-%s.%s", msg.getMid(), msg.getRid(), msg.getAttachmentType());
+ }
+ File fullImage = Paths.get(fullImageDir, imageName).toFile();
+ File mediumImage = Paths.get(mediumImageDir, imageName).toFile();
+ File smallImage = Paths.get(smallImageDir, imageName).toFile();
+ File thumbnailImage = Paths.get(thumbnailImageDir, imageName).toFile();
+ StringBuilder builder = new StringBuilder();
+ builder.append(baseUrl);
+ builder.append(msg.getAttachmentType().equals("mp4") ? "video" : "p");
+ builder.append("/").append(msg.getMid());
+ if (msg.getRid() > 0) {
+ builder.append("-").append(msg.getRid());
+ }
+ builder.append(".").append(msg.getAttachmentType());
+ String originalUrl = builder.toString();
+
+ Attachment original = getAttachment(fullImage);
+ original.setUrl(originalUrl);
+
+ Attachment medium = getAttachment(mediumImage);
+ medium.setUrl(photo.getMedium());
+ original.setMedium(medium);
+
+ Attachment small = getAttachment(smallImage);
+ small.setUrl(photo.getSmall());
+ original.setSmall(small);
+
+ Attachment thumb = getAttachment(thumbnailImage);
+ thumb.setUrl(photo.getMedium());
+ original.setThumbnail(thumb);
+
+ msg.setAttachment(original);
+ }
+ }
+
+ /**
+ * 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(fullImageDir, 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(mediumImageDir, outputFilename).toFile());
+ ImageIO.write(image0512, ext, Paths.get(smallImageDir, outputFilename).toFile());
+ ImageIO.write(image0160, ext, Paths.get(thumbnailImageDir, outputFilename).toFile());
+ }
+
+ private String getAvatarFileName(User user, String targetExtension) {
+ return String.format("%s.%s", user.getUid(), targetExtension);
+ }
+
+ private Path getAvatarPath(User user) {
+ return Paths.get(avatarDir, getAvatarFileName(user, "png"));
+ }
+
+ public void saveAvatar(String tempFilename, User user) throws IOException {
+ String ext = FilenameUtils.getExtension(tempFilename);
+ String originalName = getAvatarFileName(user, ext);
+ Path originalPath = Paths.get(avatarOriginalDir, originalName);
+ Files.move(Paths.get(tmpDir, tempFilename), originalPath, StandardCopyOption.REPLACE_EXISTING);
+ BufferedImage originalImage = ImageIO.read(originalPath.toFile());
+ String targetExt = "png";
+ ImageIO.write(Scalr.resize(originalImage, 96), targetExt, getAvatarPath(user).toFile());
+ ImageIO.write(Scalr.resize(originalImage, 32), targetExt, Paths.get(avatarSmallDir, getAvatarFileName(user, targetExt)).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;
+ }
+
+ public Attachment getAvatarMetadata(User user) throws IOException {
+ return getAttachment(getAvatarPath(user).toFile());
+ }
+
+ @Override
+ public String getBaseDirectory() {
+ return baseDir;
+ }
+
+ @Override
+ public String getImageDirectory() {
+ return imgDir;
+ }
+
+ @Override
+ public String getTemporaryDirectory() {
+ return tmpDir;
+ }
+}
diff --git a/src/main/java/com/juick/service/ImagesService.java b/src/main/java/com/juick/service/ImagesService.java
deleted file mode 100644
index 0891dca9..00000000
--- a/src/main/java/com/juick/service/ImagesService.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2008-2020, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-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.
- * Create preview images in corresponding folders.
- *
- * @param tempFilename Name of the image file in the temp folder.
- * @param outputFilename Name that will be used in the image folder.
- */
- void saveImageWithPreviews(String tempFilename, String outputFilename) throws IOException;
- /**
- * Save new avatar in all required sizes.
- *
- * @param tempFilename Name of the image file in the temp folder.
- * @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
deleted file mode 100644
index 6687a59b..00000000
--- a/src/main/java/com/juick/service/ImagesServiceImpl.java
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2008-2020, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.service;
-
-import com.juick.model.Attachment;
-import com.juick.model.Message;
-import com.juick.model.Photo;
-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 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;
- 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()));
- } 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()));
- photo.setThumbnail(String.format("%sps/%d.%s", baseUrl, msg.getMid(), msg.getAttachmentType()));
- }
- msg.setPhoto(photo);
- String imageName = String.format("%s.%s", msg.getMid(), msg.getAttachmentType());
- if (msg.getRid() > 0) {
- imageName = String.format("%s-%s.%s", msg.getMid(), msg.getRid(), msg.getAttachmentType());
- }
- File fullImage = Paths.get(imgDir, "p", imageName).toFile();
- File mediumImage = Paths.get(imgDir, "photos-1024", imageName).toFile();
- File smallImage = Paths.get(imgDir, "photos-512", imageName).toFile();
- File thumbnailImage = Paths.get(imgDir, "ps", imageName).toFile();
- StringBuilder builder = new StringBuilder();
- builder.append(baseUrl);
- builder.append(msg.getAttachmentType().equals("mp4") ? "video" : "p");
- builder.append("/").append(msg.getMid());
- if (msg.getRid() > 0) {
- builder.append("-").append(msg.getRid());
- }
- builder.append(".").append(msg.getAttachmentType());
- String originalUrl = builder.toString();
-
- Attachment original = getAttachment(fullImage);
- original.setUrl(originalUrl);
-
- Attachment medium = getAttachment(mediumImage);
- medium.setUrl(photo.getMedium());
- original.setMedium(medium);
-
- Attachment small = getAttachment(smallImage);
- small.setUrl(photo.getSmall());
- original.setSmall(small);
-
- Attachment thumb = getAttachment(thumbnailImage);
- thumb.setUrl(photo.getMedium());
- original.setThumbnail(thumb);
-
- msg.setAttachment(original);
- }
- }
-
- @Override
- 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 {
- 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;
- }
-}
diff --git a/src/main/java/com/juick/service/MessagesServiceImpl.java b/src/main/java/com/juick/service/MessagesServiceImpl.java
index b6a2edb7..a787779b 100644
--- a/src/main/java/com/juick/service/MessagesServiceImpl.java
+++ b/src/main/java/com/juick/service/MessagesServiceImpl.java
@@ -63,7 +63,7 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
@Inject
private SearchService searchService;
@Inject
- private ImagesService imagesService;
+ private StorageService storageService;
@Inject
private WebApp webApp;
@Value("${photos_url:https://i.juick.com/}")
@@ -113,7 +113,7 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
msg.setUnread(rs.getBoolean(26));
if (StringUtils.isNotEmpty(msg.getAttachmentType())) {
try {
- imagesService.setAttachmentMetadata(baseImagesUrl, msg);
+ storageService.setAttachmentMetadata(baseImagesUrl, msg);
} catch (Exception e) {
logger.warn("exception reading images for mid {} rid {}", msg.getMid(), msg.getRid(), e);
}
@@ -434,7 +434,7 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
msg.setReplyToUri(URI.create(Optional.ofNullable(rs.getString(15)).orElse(StringUtils.EMPTY)));
if (StringUtils.isNotEmpty(msg.getAttachmentType())) {
try {
- imagesService.setAttachmentMetadata(baseImagesUrl, msg);
+ storageService.setAttachmentMetadata(baseImagesUrl, msg);
} catch (Exception e) {
logger.warn("exception reading images for mid {} rid {}", msg.getMid(), msg.getRid(), e);
}
diff --git a/src/main/java/com/juick/service/StorageService.java b/src/main/java/com/juick/service/StorageService.java
new file mode 100644
index 00000000..f1f72a60
--- /dev/null
+++ b/src/main/java/com/juick/service/StorageService.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008-2020, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.juick.service;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.juick.model.Attachment;
+import com.juick.model.Message;
+import com.juick.model.User;
+
+public interface StorageService {
+
+ void setAttachmentMetadata(String baseUrl, Message msg) throws Exception;
+ /**
+ * Move attached image from temp folder to image folder.
+ * Create preview images in corresponding folders.
+ *
+ * @param tempFilename Name of the image file in the temp folder.
+ * @param outputFilename Name that will be used in the image folder.
+ */
+ void saveImageWithPreviews(String tempFilename, String outputFilename) throws IOException;
+ /**
+ * Save new avatar in all required sizes.
+ *
+ * @param tempFilename Name of the image file in the temp folder.
+ * @param user User hat is used to build image file names.
+ */
+ void saveAvatar(String tempFilename, User user) throws IOException;
+
+ public Attachment getAttachment(File imgFile) throws IOException;
+
+ public Attachment getAvatarMetadata(User user) throws IOException;
+
+ String getBaseDirectory();
+
+ String getTemporaryDirectory();
+
+ String getImageDirectory();
+}
diff --git a/src/main/java/com/juick/util/ImageUtils.java b/src/main/java/com/juick/util/ImageUtils.java
deleted file mode 100644
index 6ba6882c..00000000
--- a/src/main/java/com/juick/util/ImageUtils.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2008-2020, Juick
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package com.juick.util;
-
-import com.juick.model.Attachment;
-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 javax.imageio.ImageIO;
-import javax.imageio.ImageReader;
-import javax.imageio.stream.ImageInputStream;
-import java.awt.image.BufferedImage;
-import java.io.File;
-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;
-
-public class ImageUtils {
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/juick/www/WebApp.java b/src/main/java/com/juick/www/WebApp.java
index cebedb9b..85e21b1a 100644
--- a/src/main/java/com/juick/www/WebApp.java
+++ b/src/main/java/com/juick/www/WebApp.java
@@ -19,6 +19,7 @@ package com.juick.www;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -30,9 +31,11 @@ import java.util.stream.Stream;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
+import com.juick.model.Attachment;
import com.juick.model.Message;
import com.juick.model.Tag;
import com.juick.model.User;
+import com.juick.service.StorageService;
import com.juick.service.TagService;
import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.template.PebbleTemplate;
@@ -49,6 +52,8 @@ import org.springframework.web.util.UriComponentsBuilder;
*/
@Component
public class WebApp {
+ @Inject
+ private StorageService storageService;
@Inject
private TagService tagService;
@Inject
diff --git a/src/main/java/com/juick/www/api/Post.java b/src/main/java/com/juick/www/api/Post.java
index c993ebe3..bced92bf 100644
--- a/src/main/java/com/juick/www/api/Post.java
+++ b/src/main/java/com/juick/www/api/Post.java
@@ -17,38 +17,46 @@
package com.juick.www.api;
+import java.net.URI;
+import java.net.URL;
+import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.validation.constraints.NotNull;
+
+import com.juick.ActivityPubManager;
+import com.juick.CommandsManager;
+import com.juick.model.CommandResult;
import com.juick.model.Message;
import com.juick.model.Reaction;
import com.juick.model.Status;
import com.juick.model.User;
-import com.juick.model.CommandResult;
-import com.juick.ActivityPubManager;
-import com.juick.CommandsManager;
-import com.juick.util.HttpBadRequestException;
-import com.juick.util.HttpForbiddenException;
-import com.juick.util.HttpNotFoundException;
-import com.juick.util.HttpUtils;
import com.juick.service.MessagesService;
+import com.juick.service.StorageService;
import com.juick.service.UserService;
import com.juick.service.activities.UpdateEvent;
import com.juick.service.security.annotation.Visitor;
+import com.juick.util.HttpBadRequestException;
+import com.juick.util.HttpForbiddenException;
+import com.juick.util.HttpNotFoundException;
+import com.juick.util.HttpUtils;
+
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
-import javax.inject.Inject;
-import javax.validation.constraints.NotNull;
-import java.net.URI;
-import java.net.URL;
-import java.util.List;
-import java.util.Optional;
-
/**
* Created by vt on 24/11/2016.
*/
@@ -60,8 +68,8 @@ public class Post {
private UserService userService;
@Inject
private MessagesService messagesService;
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
+ @Inject
+ private StorageService storageService;
@Inject
CommandsManager commandsManager;
@Inject
@@ -78,7 +86,7 @@ public class Post {
@RequestParam(required = false) MultipartFile attach) throws Exception {
body = body.replace("\r", StringUtils.EMPTY);
- URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
+ URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, storageService.getTemporaryDirectory());
if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) {
URI juickUri = URI.create(img);
@@ -87,7 +95,7 @@ public class Post {
} else {
try {
URL imgUrl = new URL(img);
- attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir);
+ attachmentFName = HttpUtils.downloadImage(imgUrl, storageService.getTemporaryDirectory());
} catch (Exception e) {
logger.error("DOWNLOAD ERROR", e);
throw new HttpBadRequestException();
@@ -135,11 +143,11 @@ public class Post {
throw new HttpForbiddenException();
}
- URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir);
+ URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, storageService.getTemporaryDirectory());
if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) {
try {
- attachmentFName = HttpUtils.downloadImage(new URL(img), tmpDir);
+ attachmentFName = HttpUtils.downloadImage(new URL(img), storageService.getTemporaryDirectory());
} catch (Exception e) {
logger.error("DOWNLOAD ERROR", e);
throw new HttpBadRequestException();
diff --git a/src/main/java/com/juick/www/api/Service.java b/src/main/java/com/juick/www/api/Service.java
index fc0132b1..f7c7a4aa 100644
--- a/src/main/java/com/juick/www/api/Service.java
+++ b/src/main/java/com/juick/www/api/Service.java
@@ -27,6 +27,7 @@ import com.juick.util.HttpBadRequestException;
import com.juick.util.HttpForbiddenException;
import com.juick.service.EmailService;
import com.juick.service.MessagesService;
+import com.juick.service.StorageService;
import com.juick.service.UserService;
import com.juick.service.component.AccountVerificationEvent;
import com.juick.service.security.annotation.Visitor;
@@ -77,8 +78,8 @@ public class Service {
private ApplicationEventPublisher applicationEventPublisher;
@Inject
private User serviceUser;
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
+ @Inject
+ private StorageService storageService;
@Value("${banned_emails:}")
private String[] ignoredEmails;
@Inject
@@ -139,7 +140,7 @@ public class Service {
try {
logger.info("got inputstream: {}", a.getInputStream());
FileOutputStream fos = new FileOutputStream(
- Paths.get(tmpDir, attachmentFName[0]).toString());
+ Paths.get(storageService.getTemporaryDirectory(), attachmentFName[0]).toString());
IOUtils.copy(a.getInputStream(), fos);
fos.close();
} catch (IOException e) {
diff --git a/src/main/java/com/juick/www/api/Users.java b/src/main/java/com/juick/www/api/Users.java
index 0294ec53..49a4494f 100644
--- a/src/main/java/com/juick/www/api/Users.java
+++ b/src/main/java/com/juick/www/api/Users.java
@@ -63,9 +63,7 @@ public class Users {
@Inject
private WebApp webApp;
@Inject
- private ImagesService imagesService;
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
+ private StorageService storageService;
@Inject
private ApplicationEventPublisher applicationEventPublisher;
@@ -168,9 +166,9 @@ public class Users {
@PostMapping("/api/me/upload")
public void updateInfo(@Visitor User visitor,
@RequestParam MultipartFile avatar) throws IOException {
- String avatarTmpPath = HttpUtils.receiveMultiPartFile(avatar, tmpDir).getHost();
+ String avatarTmpPath = HttpUtils.receiveMultiPartFile(avatar, storageService.getTemporaryDirectory()).getHost();
if (StringUtils.isNotEmpty(avatarTmpPath)) {
- imagesService.saveAvatar(avatarTmpPath, visitor.getUid());
+ storageService.saveAvatar(avatarTmpPath, visitor);
applicationEventPublisher.publishEvent(new UpdateUserEvent(this, visitor));
}
}
diff --git a/src/main/java/com/juick/www/controllers/Settings.java b/src/main/java/com/juick/www/controllers/Settings.java
index 50b4ea14..a5597735 100644
--- a/src/main/java/com/juick/www/controllers/Settings.java
+++ b/src/main/java/com/juick/www/controllers/Settings.java
@@ -63,8 +63,6 @@ import java.util.stream.IntStream;
public class Settings {
private static final Logger logger = LoggerFactory.getLogger(Settings.class);
- @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
- private String tmpDir;
@Inject
private TagService tagService;
@Inject
@@ -76,7 +74,7 @@ public class Settings {
@Inject
private TelegramService telegramService;
@Inject
- private ImagesService imagesService;
+ private StorageService storageService;
@Inject
private WebApp webApp;
@Inject
@@ -166,9 +164,9 @@ public class Settings {
visitor.setCountry(request.getParameter("country"));
visitor.setUrl(request.getParameter("url"));
visitor.setDescription(request.getParameter("descr"));
- String avatarTmpPath = HttpUtils.receiveMultiPartFile(avatar, tmpDir).getHost();
+ String avatarTmpPath = HttpUtils.receiveMultiPartFile(avatar, storageService.getTemporaryDirectory()).getHost();
if (StringUtils.isNotEmpty(avatarTmpPath)) {
- imagesService.saveAvatar(avatarTmpPath, visitor.getUid());
+ storageService.saveAvatar(avatarTmpPath, visitor);
}
if (userService.updateUserInfo(visitor)) {
result = String.format("Your info is updated.
Back to blog.
", visitor.getName());
diff --git a/src/main/java/com/juick/www/rss/MessagesView.java b/src/main/java/com/juick/www/rss/MessagesView.java
index 6355c47d..2c7d4a7d 100644
--- a/src/main/java/com/juick/www/rss/MessagesView.java
+++ b/src/main/java/com/juick/www/rss/MessagesView.java
@@ -17,14 +17,29 @@
package com.juick.www.rss;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
import com.juick.model.Attachment;
import com.juick.model.Message;
import com.juick.model.User;
-import com.juick.service.ImagesService;
+import com.juick.service.StorageService;
+import com.juick.util.MessageUtils;
+import com.juick.www.WebApp;
import com.juick.www.rss.extension.JuickModule;
import com.juick.www.rss.extension.JuickModuleImpl;
-import com.juick.www.WebApp;
-import com.juick.util.MessageUtils;
import com.rometools.modules.atom.modules.AtomLinkModule;
import com.rometools.modules.atom.modules.AtomLinkModuleImpl;
import com.rometools.modules.mediarss.MediaEntryModuleImpl;
@@ -47,21 +62,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.view.feed.AbstractRssFeedView;
-import javax.annotation.Nonnull;
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
/**
* Created by vitalyster on 13.12.2016.
*/
@@ -70,9 +70,9 @@ public class MessagesView extends AbstractRssFeedView {
private static final Logger logger = LoggerFactory.getLogger(Feeds.class);
@Inject
- private WebApp webApp;
+ private StorageService storageService;
@Inject
- private ImagesService imagesService;
+ private WebApp webApp;
@PostConstruct
public void init() {
@@ -99,12 +99,11 @@ public class MessagesView extends AbstractRssFeedView {
String link = String.format("https://juick.com/%s/", user.getName());
feed.setLink(link);
try {
+ Attachment avatar = storageService.getAvatarMetadata(user);
Image rssImage = new Image();
rssImage.setUrl(webApp.getAvatarUrl(user));
rssImage.setTitle(title);
rssImage.setLink(link);
- Attachment avatar;
- avatar = imagesService.getImageMetadata(rssImage.getUrl());
rssImage.setHeight(avatar.getHeight());
rssImage.setWidth(avatar.getWidth());
feed.setImage(rssImage);
diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java
index dc88cfbd..5aee3af8 100644
--- a/src/test/java/com/juick/server/tests/ServerTests.java
+++ b/src/test/java/com/juick/server/tests/ServerTests.java
@@ -132,6 +132,7 @@ import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.net.URI;
import java.nio.charset.StandardCharsets;
+import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -203,7 +204,7 @@ public class ServerTests {
@Inject
private KeystoreManager keystoreManager;
@Inject
- private ImagesService imagesService;
+ private StorageService storageService;
@Inject
private PebbleEngine pebbleEngine;
@Value("${ios_app_id:}")
@@ -281,7 +282,7 @@ public class ServerTests {
@BeforeEach
public void setUp() throws Exception {
- String imgDir = imagesService.getImageDirectory();
+ String imgDir = storageService.getImageDirectory();
FileSystemUtils.deleteRecursively(Paths.get(imgDir, "p"));
FileSystemUtils.deleteRecursively(Paths.get(imgDir, "photos-1024"));
FileSystemUtils.deleteRecursively(Paths.get(imgDir, "photos-512"));
@@ -312,7 +313,7 @@ public class ServerTests {
@AfterEach
public void teardown() throws IOException {
- String imgDir = imagesService.getImageDirectory();
+ String imgDir = storageService.getImageDirectory();
FileSystemUtils.deleteRecursively(Paths.get(imgDir, "p"));
FileSystemUtils.deleteRecursively(Paths.get(imgDir, "photos-1024"));
FileSystemUtils.deleteRecursively(Paths.get(imgDir, "photos-512"));
@@ -761,7 +762,7 @@ public class ServerTests {
@Test
public void protocolTests() throws Exception {
- String tmpDir = imagesService.getTemporaryDirectory();
+ String tmpDir = storageService.getTemporaryDirectory();
User user = userService.createUser("me", "secret").orElseThrow(IllegalStateException::new);
Tag yo = tagService.getTag("yo", true);
Message msg = commandsManager
@@ -1126,9 +1127,12 @@ public class ServerTests {
@Test
public void attachmentSizeTests() throws IOException {
- Attachment attachment = imagesService.getImageMetadata(invisiblePixel.getURI().toASCIIString());
- assertThat(attachment.getHeight(), is(1));
- assertThat(attachment.getWidth(), is(1));
+ String tmpPng = "tmp.png";
+ Files.copy(defaultAvatar.getFile().toPath(), Paths.get(storageService.getTemporaryDirectory(), tmpPng), StandardCopyOption.REPLACE_EXISTING);
+ storageService.saveAvatar(tmpPng, serviceUser);
+ Attachment attachment = storageService.getAvatarMetadata(serviceUser);
+ assertThat(attachment.getHeight(), is(96));
+ assertThat(attachment.getWidth(), is(96));
}
@Test
@@ -1188,7 +1192,7 @@ public class ServerTests {
@Test
public void cmykJpegShouldBeProcessedCorrectly() throws Exception {
- String imgDir = imagesService.getImageDirectory();
+ String imgDir = storageService.getImageDirectory();
CommandResult postJpgCmyk = commandsManager.processCommand(ugnich, "YO", cmykJpeg.getURI());
assertThat(postJpgCmyk.getNewMessage().isPresent(), is(true));
int mid = postJpgCmyk.getNewMessage().get().getMid();
@@ -1204,7 +1208,7 @@ public class ServerTests {
@Test
public void JpegWithoutJfifShouldBeProcessedCorrectly() throws Exception {
- String imgDir = imagesService.getImageDirectory();
+ String imgDir = storageService.getImageDirectory();
CommandResult postJpgCmyk = commandsManager.processCommand(ugnich, "YO", nojfif.getURI());
assertThat(postJpgCmyk.getNewMessage().isPresent(), is(true));
int mid = postJpgCmyk.getNewMessage().get().getMid();
@@ -1220,8 +1224,8 @@ public class ServerTests {
@Test
public void JpegFromJuickUriShouldBeProcessedCorrectly() throws Exception {
- String imgDir = imagesService.getImageDirectory();
- String tmpDir = imagesService.getTemporaryDirectory();
+ String imgDir = storageService.getImageDirectory();
+ String tmpDir = storageService.getTemporaryDirectory();
Path tmpFile = Paths.get(tmpDir, "2915104.jpg");
Files.copy(Paths.get(new ClassPathResource("2915104.jpg").getURI()), tmpFile,
StandardCopyOption.REPLACE_EXISTING);
@@ -1248,7 +1252,7 @@ public class ServerTests {
@Test
public void changeExtensionWhenReceiveFileWithWrongContentType() throws Exception {
- String tmpDir = imagesService.getTemporaryDirectory();
+ String tmpDir = storageService.getTemporaryDirectory();
Path pngOutput = Paths.get(tmpDir, "cmyk.png");
Files.deleteIfExists(pngOutput);
Files.copy(Paths.get(cmykJpeg.getURI()), pngOutput);
@@ -2043,7 +2047,7 @@ public class ServerTests {
@Test
public void changeProfileOverApi() throws Exception {
- String imgDir = imagesService.getImageDirectory();
+ String imgDir = storageService.getImageDirectory();
ClassPathResource defaultAvatar = new ClassPathResource("static/av-96.png");
String hash = DigestUtils.md5DigestAsHex(IOUtils.toByteArray(defaultAvatar.getInputStream()));
assertThat(webApp.getAvatarUrl(userService.getUserByName(freefdName)),
--
cgit v1.2.3