Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

πŸš€ Feature image enhancement #85

Open
wants to merge 10 commits into
base: feature
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions mysql/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,12 @@ CREATE TABLE likes
FOREIGN KEY (user_id) REFERENCES users (id),
FOREIGN KEY (post_id) REFERENCES posts (id) ON DELETE CASCADE
);


CREATE TABLE images
(
id BIGINT AUTO_INCREMENT,
image_url varchar(500) NOT NULL,
filename varchar(255) NOT NULL,
Comment on lines +65 to +66
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

두 컬럼 λͺ¨λ‘ unique인 것 같은데 λ§žμ„κΉŒμš”?

CONSTRAINT images_pk PRIMARY KEY (id)
);
310 changes: 155 additions & 155 deletions src/docs/asciidoc/index.html

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/main/java/com/backendoori/ootw/OotwApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
@ConfigurationPropertiesScan
public class OotwApplication {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.backendoori.ootw.avatar.dto.AvatarItemRequest;
import com.backendoori.ootw.avatar.dto.AvatarItemResponse;
import com.backendoori.ootw.avatar.service.AvatarItemService;
import com.backendoori.ootw.common.validation.Image;
import com.backendoori.ootw.image.validation.Image;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.backendoori.ootw.avatar.domain.ItemType;
import com.backendoori.ootw.avatar.domain.Sex;
import com.backendoori.ootw.common.validation.Enum;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μ›λž˜λŠ” μ—¬κΈ°μ„œ Enum μ–΄λ…Έν…Œμ΄μ…˜ μ •μ˜λœ ν΄λž˜μŠ€κ°€ μ§€κΈˆ 바뀐 PRμ—λŠ” src/main/java/com/backendoori/ootw/image/validation/Enum.java 여기인 걸둜 μ•„λŠ”λ° κ²½λ‘œκ°€ μ•ˆλ°”λ€Œμ–΄μ„œ 그런 κ²ƒμΌκΉŒμš”..?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인이 ν•„μš”ν•˜κ² λ„€μš”

import jakarta.validation.constraints.NotNull;

public record AvatarItemRequest(
@Enum(enumClass = ItemType.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import com.backendoori.ootw.avatar.dto.AvatarItemRequest;
import com.backendoori.ootw.avatar.dto.AvatarItemResponse;
import com.backendoori.ootw.avatar.repository.AvatarItemRepository;
import com.backendoori.ootw.common.image.ImageFile;
import com.backendoori.ootw.common.image.ImageService;
import com.backendoori.ootw.common.image.exception.SaveException;
import com.backendoori.ootw.image.dto.ImageFile;
import com.backendoori.ootw.image.service.ImageService;
import com.backendoori.ootw.image.exception.SaveException;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand Down

This file was deleted.

33 changes: 33 additions & 0 deletions src/main/java/com/backendoori/ootw/image/domain/Image.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.backendoori.ootw.image.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Table(name = "images")
@Getter
@Entity
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Image {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "image_url", unique = true)
private String ImageUrl;

@Column(name = "filename")
private String fileName;
Comment on lines +30 to +31
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fileName도 uniqueλ©΄ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€


}
12 changes: 12 additions & 0 deletions src/main/java/com/backendoori/ootw/image/dto/ImageFile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.backendoori.ootw.image.dto;

import com.backendoori.ootw.image.domain.Image;

public record ImageFile(
String url,
String fileName
) {
public static ImageFile from(Image image){
return new ImageFile(image.getImageUrl(), image.getFileName());
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.backendoori.ootw.common.image.exception;
package com.backendoori.ootw.image.exception;

import com.backendoori.ootw.exception.ErrorResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ImageControllerAdvice {

private static final String IMAGE_RELATED_EXCEPTION = "μ—…λ‘œλ“œ μš”μ²­ 쀑 λ¬Έμ œκ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.";
Expand All @@ -30,4 +33,5 @@ public ResponseEntity<ErrorResponse> handleSaveException(SaveException e) {
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY)
.body(errorResponse);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.backendoori.ootw.common.image.exception;
package com.backendoori.ootw.image.exception;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.backendoori.ootw.common.image.exception;
package com.backendoori.ootw.image.exception;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.backendoori.ootw.image.repository;

import com.backendoori.ootw.image.domain.Image;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ImageRepository extends JpaRepository<Image, Long> {

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.backendoori.ootw.common.image;
package com.backendoori.ootw.image.service;

import com.backendoori.ootw.image.dto.ImageFile;
import org.springframework.web.multipart.MultipartFile;

public interface ImageService {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.backendoori.ootw.image.service;

import java.util.ArrayList;
import java.util.List;
import com.backendoori.ootw.avatar.domain.AvatarItem;
import com.backendoori.ootw.avatar.repository.AvatarItemRepository;
import com.backendoori.ootw.image.domain.Image;
import com.backendoori.ootw.image.repository.ImageRepository;
import com.backendoori.ootw.post.domain.Post;
import com.backendoori.ootw.post.repository.PostRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class ImageUpdateService {

private final PostRepository postRepository;
private final AvatarItemRepository avatarItemRepository;
private final ImageRepository imageRepository;
private final ImageService imageService;
public static final String TIMEZONE = "Asia/Seoul";
public static final String CRON = "0 20 00 10 * ?";

@Scheduled(cron = CRON, zone = TIMEZONE) // 맀월 20일 μ˜€μ „ 00μ‹œ 10뢄에 μ‹€ν–‰
@Async
@Transactional
public void deleteUnused(){
Comment on lines +28 to +31
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ‘


List<String> urls = new ArrayList<>();

postRepository.findAll()
.stream()
.map(Post::getImageUrl)
.forEach(urls::add);

avatarItemRepository.findAll()
.stream()
.map(AvatarItem::getImageUrl)
.forEach(urls::add);

List<Image> images = imageRepository.findAll();

for(Image image : images){
if(!urls.contains(image.getImageUrl())){
imageService.delete(image.getFileName());
}
}
Comment on lines +33 to +51
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

배열을 μ‚¬μš©ν•˜λ‹€ λ³΄λ‹ˆ 이미지 μˆ˜κ°€ λ§Žμ„ κ²½μš°μ—λŠ” μ‹œκ°„μ΄ μƒλ‹Ήνžˆ 였래걸릴 수 μžˆκ² λ„€μš”
그리고 47번 ν–‰μ—μ„œμ˜ forλ¬Έ λ‘œμ§λ„ stream으둜 μ²˜λ¦¬ν•˜λ©΄ 쒋을 것 κ°™μ•„μš”
filterμ—μ„œ deleteλ₯Ό ν•˜λ©΄ λ˜λ‹ˆκΉŒμš”


}

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
package com.backendoori.ootw.common.image;
package com.backendoori.ootw.image.service;

import static com.backendoori.ootw.common.image.exception.ImageException.IMAGE_ROLLBACK_FAIL_MESSAGE;
import static com.backendoori.ootw.common.image.exception.ImageException.IMAGE_UPLOAD_FAIL_MESSAGE;
import static com.backendoori.ootw.common.validation.ImageValidator.validateImage;
import static com.backendoori.ootw.image.validation.ImageValidator.validateImage;

import java.io.InputStream;
import java.nio.file.Path;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import com.backendoori.ootw.common.image.exception.ImageException;
import com.backendoori.ootw.image.domain.Image;
import com.backendoori.ootw.image.dto.ImageFile;
import com.backendoori.ootw.image.exception.ImageException;
import com.backendoori.ootw.config.MiniOConfig;
import com.backendoori.ootw.image.repository.ImageRepository;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.http.Method;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

Expand All @@ -29,28 +34,46 @@ public class MiniOImageServiceImpl implements ImageService {

private final MinioClient minioClient;
private final MiniOConfig miniOConfig;
private Path path;
private final ImageRepository imageRepository;

@Override
public ImageFile upload(MultipartFile file) {
validateImage(file);
String randomFileName = getUniqueFileName(file);
try {
path = Path.of(file.getOriginalFilename());
InputStream inputStream = file.getInputStream();
String contentType = file.getContentType();
PutObjectArgs args = PutObjectArgs.builder()
.bucket(miniOConfig.getBucket())
.object(path.toString())
.object(randomFileName)
.stream(inputStream, inputStream.available(), -1)
.contentType(contentType)
.build();
minioClient.putObject(args);
return new ImageFile(getUrl(), path.toString());
String url = getUrl(randomFileName);
Image image = saveImage(url, randomFileName);
imageRepository.save(image);
return ImageFile.from(image);
} catch (Exception e) {
throw new ImageException(IMAGE_UPLOAD_FAIL_MESSAGE);
throw new ImageException(ImageException.IMAGE_UPLOAD_FAIL_MESSAGE);
}
}

private static Image saveImage(String url, String randomFileName) {
return Image.builder()
.ImageUrl(url)
.fileName(randomFileName)
.build();
}

@NotNull
private static String getUniqueFileName(MultipartFile file) {
String randomUUID = UUID.randomUUID().toString();
String originalFileName = file.getOriginalFilename();

return randomUUID + originalFileName;
}

Comment on lines +70 to +75
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ‘

Comment on lines +69 to +75
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ν•΄λ‹Ή λΆ€λΆ„μ˜ νŒŒλΌλ―Έν„°λ‘œ file μžμ²΄κ°€ μ•„λ‹Œ file name을 λ°”λ‘œ μ£ΌλŠ” 것도 쒋을 것 κ°™μ•„μš”
그리고 uuidλž‘ original file name 사이에 κ΅¬λΆ„μžκ°€ λ“€μ–΄κ°€λ©΄ 쒋을 것 κ°™μ•„μš”
μ΅œμ’…μ μœΌλ‘œ λ§Œλ“  file name은 s3 name convention을 μ€€μˆ˜ν•˜λŠ”μ§€ 확인해보면 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€

@Override
public void delete(String fileName) {
try {
Expand All @@ -59,21 +82,21 @@ public void delete(String fileName) {
.object(fileName)
.build());
} catch (Exception e) {
throw new ImageException(IMAGE_ROLLBACK_FAIL_MESSAGE);
throw new ImageException(ImageException.IMAGE_ROLLBACK_FAIL_MESSAGE);
}
}

private String getUrl() {
private String getUrl(String fileName) {
try {
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(miniOConfig.getBucket())
.object(path.toString())
.object(fileName)
.expiry(DURATION, TimeUnit.HOURS)
.build());
} catch (Exception e) {
throw new ImageException(IMAGE_UPLOAD_FAIL_MESSAGE);
throw new ImageException(ImageException.IMAGE_UPLOAD_FAIL_MESSAGE);
}
}

Expand Down
20 changes: 20 additions & 0 deletions src/main/java/com/backendoori/ootw/image/validation/Enum.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.backendoori.ootw.image.validation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;

@Target(value = {ElementType.PARAMETER, ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)
public @interface Enum {

String message() default "μœ νš¨ν•˜μ§€ μ•Šμ€ κ°’μž…λ‹ˆλ‹€ λ‹€μ‹œ μž…λ ₯ν•΄μ£Όμ„Έμš”";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends java.lang.Enum<?>> enumClass();
Comment on lines +15 to +18
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μΈν„°νŽ˜μ΄μŠ€μ—μ„œλŠ” λͺ¨λ“  행에 λŒ€ν•œ κ°œν–‰μ΄ ν•„μš”ν•  것 κ°™μ•„μš”


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.backendoori.ootw.image.validation;

import java.util.Arrays;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class EnumValidator implements ConstraintValidator<Enum, String> {

private Enum annotation;

@Override
public void initialize(Enum constraintAnnotation) {
this.annotation = constraintAnnotation;
}

@Override
public boolean isValid(String type, ConstraintValidatorContext context) {
if (type == null) {
return false;
}

return Arrays.stream(this.annotation.enumClass().getEnumConstants())
.anyMatch(e -> e.name().equals(type));
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.backendoori.ootw.common.validation;
package com.backendoori.ootw.image.validation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.backendoori.ootw.common.validation;
package com.backendoori.ootw.image.validation;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.backendoori.ootw.common.validation;
package com.backendoori.ootw.image.validation;

import io.jsonwebtoken.lang.Assert;
import lombok.AccessLevel;
Expand Down
Loading
Loading