diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResource.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResource.java index 3dff04d..8bb119e 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResource.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResource.java @@ -22,7 +22,6 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.validation.Valid; -import java.io.IOException; import java.util.UUID; import static org.springframework.http.HttpStatus.OK; @@ -110,7 +109,7 @@ public class UserResource { } @GetMapping(path = "{userId}/profile-image/{filename}", produces = MediaType.IMAGE_JPEG_VALUE) - public byte[] getProfileImageByUserId(@PathVariable UUID userId, @PathVariable String filename) throws IOException { + public byte[] getProfileImageByUserId(@PathVariable UUID userId, @PathVariable String filename) { return userService.getImageByUserId(userId, filename); } diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/exception/ExceptionHandling.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/exception/ExceptionHandling.java index f726ee8..cf52831 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/exception/ExceptionHandling.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/exception/ExceptionHandling.java @@ -117,8 +117,8 @@ public class ExceptionHandling { return createHttpResponse(NOT_FOUND, exception.getMessage()); } - @ExceptionHandler(IOException.class) - public ResponseEntity iOException(IOException exception) { + @ExceptionHandler({IOException.class, ImageStorageException.class}) + public ResponseEntity fileRelatedException(Exception exception) { log.error(exception.getMessage()); return createHttpResponse(INTERNAL_SERVER_ERROR, ERROR_PROCESSING_FILE); } diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/exception/domain/ImageStorageException.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/exception/domain/ImageStorageException.java new file mode 100644 index 0000000..f6d4789 --- /dev/null +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/exception/domain/ImageStorageException.java @@ -0,0 +1,11 @@ +package net.shyshkin.study.fullstack.supportportal.backend.exception.domain; + +public class ImageStorageException extends RuntimeException { + public ImageStorageException(String message) { + super(message); + } + + public ImageStorageException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/FileSystemProfileImageService.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/FileSystemProfileImageService.java new file mode 100644 index 0000000..b077dc1 --- /dev/null +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/FileSystemProfileImageService.java @@ -0,0 +1,61 @@ +package net.shyshkin.study.fullstack.supportportal.backend.service; + +import lombok.extern.slf4j.Slf4j; +import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.ImageStorageException; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; +import org.springframework.util.FileSystemUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.UUID; + +import static net.shyshkin.study.fullstack.supportportal.backend.constant.FileConstant.*; + +@Slf4j +@Service +@Profile("!image-s3") +public class FileSystemProfileImageService implements ProfileImageService { + + @Override + public byte[] retrieveProfileImage(UUID userId, String filename) { + + Path userProfileImagePath = Paths + .get(USER_FOLDER, userId.toString(), filename); + try { + return Files.readAllBytes(userProfileImagePath); + } catch (IOException exception) { + throw new ImageStorageException("Can not retrieve image for user " + userId + " from file " + filename, exception); + } + } + + @Override + public String persistProfileImage(UUID userId, MultipartFile profileImage, String filename) { + Path userFolder = Paths.get(USER_FOLDER, userId.toString()); + try { + if (Files.notExists(userFolder)) { + Files.createDirectories(userFolder); + log.debug(DIRECTORY_CREATED); + } + profileImage.transferTo(userFolder.resolve(USER_IMAGE_FILENAME)); + log.debug(FILE_SAVED_IN_FILE_SYSTEM + profileImage.getOriginalFilename()); + + } catch (IOException exception) { + throw new ImageStorageException("Can not persist image for user " + userId + " from file " + profileImage, exception); + } + return null; + } + + @Override + public void clearUserStorage(UUID userId) { + Path userFolder = Paths.get(USER_FOLDER, userId.toString()); + try { + FileSystemUtils.deleteRecursively(userFolder); + } catch (IOException exception) { + throw new ImageStorageException("Can not delete folder for user " + userId, exception); + } + } +} diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfileImageService.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfileImageService.java new file mode 100644 index 0000000..9fcf5f5 --- /dev/null +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfileImageService.java @@ -0,0 +1,15 @@ +package net.shyshkin.study.fullstack.supportportal.backend.service; + +import org.springframework.web.multipart.MultipartFile; + +import java.util.UUID; + +public interface ProfileImageService { + + byte[] retrieveProfileImage(UUID userId, String filename); + + String persistProfileImage(UUID userId, MultipartFile profileImage, String filename); + + void clearUserStorage(UUID userId); + +} diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserService.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserService.java index 8045e42..da54840 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserService.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserService.java @@ -7,7 +7,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; import java.util.UUID; public interface UserService extends UserDetailsService { @@ -32,7 +31,7 @@ public interface UserService extends UserDetailsService { User updateProfileImage(UUID userId, MultipartFile profileImage); - byte[] getImageByUserId(UUID userId, String filename) throws IOException; + byte[] getImageByUserId(UUID userId, String filename); byte[] getDefaultProfileImage(UUID userId); } diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserServiceImpl.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserServiceImpl.java index 08e3b1e..65802e9 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserServiceImpl.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserServiceImpl.java @@ -19,17 +19,12 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import org.springframework.util.FileSystemUtils; import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import javax.annotation.PostConstruct; import javax.transaction.Transactional; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.time.LocalDateTime; import java.util.List; import java.util.Objects; @@ -55,6 +50,7 @@ public class UserServiceImpl implements UserService { private final EmailService emailService; private final UserMapper userMapper; private final RestTemplateBuilder restTemplateBuilder; + private final ProfileImageService profileImageService; private RestTemplate restTemplate; @@ -188,30 +184,17 @@ public class UserServiceImpl implements UserService { throw new NotAnImageFileException(profileImage.getOriginalFilename() + " is not an image file. Please upload an image"); } - Path userFolder = Paths.get(USER_FOLDER, user.getUserId().toString()); - try { - if (Files.notExists(userFolder)) { - Files.createDirectories(userFolder); - log.debug(DIRECTORY_CREATED); - } - profileImage.transferTo(userFolder.resolve(USER_IMAGE_FILENAME)); - log.debug(FILE_SAVED_IN_FILE_SYSTEM + profileImage.getOriginalFilename()); - user.setProfileImageUrl(generateProfileImageUrl(user.getUserId())); - userRepository.save(user); + String imageUrl = profileImageService.persistProfileImage(user.getUserId(), profileImage, USER_IMAGE_FILENAME); - } catch (IOException exception) { - log.error("Can't save to file", exception); - } + if (imageUrl == null) + imageUrl = generateProfileImageUrl(user.getUserId()); + + user.setProfileImageUrl(imageUrl); + userRepository.save(user); } - private void deleteProfileImageFolder(User user) { - - Path userFolder = Paths.get(USER_FOLDER, user.getUserId().toString()); - try { - FileSystemUtils.deleteRecursively(userFolder); - } catch (IOException exception) { - log.error("Can't delete folder", exception); - } + private void clearUserStorage(User user) { + profileImageService.clearUserStorage(user.getUserId()); } @Override @@ -243,7 +226,7 @@ public class UserServiceImpl implements UserService { .findByUserId(userId) .orElseThrow(() -> new UserNotFoundException("User was not found")); - deleteProfileImageFolder(userToBeDeleted); + clearUserStorage(userToBeDeleted); userRepository.delete(userToBeDeleted); } @@ -270,15 +253,12 @@ public class UserServiceImpl implements UserService { } @Override - public byte[] getImageByUserId(UUID userId, String filename) throws IOException { + public byte[] getImageByUserId(UUID userId, String filename) { if (!userRepository.existsByUserId(userId)) { throw new UserNotFoundException(USER_NOT_FOUND_MSG); } - - Path userProfileImagePath = Paths - .get(USER_FOLDER, userId.toString(), filename); - return Files.readAllBytes(userProfileImagePath); + return profileImageService.retrieveProfileImage(userId, filename); } @Override