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] S3 로직을 리팩토링하고 사용자 프로필 사진 삭제를 구현한다 #23

Merged
merged 3 commits into from
Oct 21, 2023
Merged
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
55 changes: 55 additions & 0 deletions src/main/java/daybyquest/global/s3/S3Images.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package daybyquest.global.s3;

import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.DeleteObjectRequest;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import daybyquest.global.error.exception.InvalidFileException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class S3Images {

private final AmazonS3 amazonS3;

private final String bucket;

public S3Images(final AmazonS3 amazonS3, @Value("${aws.bucket}") final String bucket) {
this.amazonS3 = amazonS3;
this.bucket = bucket;
}

public void upload(final String category, final String identifier, final InputStream imageStream) {
try {
final String key = category + "/" + identifier;
final ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(imageStream.available());
final PutObjectRequest putObjectRequest = new PutObjectRequest(
bucket, key, imageStream, metadata
);
amazonS3.putObject(putObjectRequest);
} catch (IOException e) {
throw new InvalidFileException();
}
}

public void remove(final String category, final String identifier) {
final String key = category + "/" + identifier;
final DeleteObjectRequest deleteObjectRequest = new DeleteObjectRequest(bucket, key);
amazonS3.deleteObject(deleteObjectRequest);
}

public String getPublicUrl(final String category, final String identifier, final Date expiration) {
final String key = category + "/" + identifier;
final GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, key)
.withMethod(HttpMethod.GET)
.withExpiration(expiration);
return amazonS3.generatePresignedUrl(request).toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package daybyquest.user.application;

import daybyquest.image.vo.BaseImageProperties;
import daybyquest.image.vo.Image;
import daybyquest.user.domain.User;
import daybyquest.user.domain.UserImages;
import daybyquest.user.domain.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class DeleteUserImageService {

private final UserRepository userRepository;

private final UserImages userImages;

private final BaseImageProperties baseImageProperties;

public DeleteUserImageService(final UserRepository userRepository, final UserImages userImages,
final BaseImageProperties baseImageProperties) {
this.userRepository = userRepository;
this.userImages = userImages;
this.baseImageProperties = baseImageProperties;
}

@Transactional
public void invoke(final Long loginId) {
final User user = userRepository.getById(loginId);
userImages.remove(user.getImageIdentifier());
user.updateImage(new Image(baseImageProperties.getUserIdentifier()));
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
import daybyquest.user.domain.UserImages;
import daybyquest.user.domain.UserRepository;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Service
@Slf4j
public class UpdateUserImageService {

private final UserRepository userRepository;
Expand All @@ -28,14 +31,23 @@ public UpdateUserImageService(final UserRepository userRepository,
public void invoke(final Long loginId, final MultipartFile file) {
final User user = userRepository.getById(loginId);
final String oldIdentifier = user.getImageIdentifier();
final String uuid = UUID.randomUUID().toString();
final String identifier = uuid + file.getOriginalFilename();
final String identifier = createIdentifier(file.getOriginalFilename());
userImages.upload(identifier, getInputStream(file));
user.updateImage(new Image(identifier));
userImages.remove(oldIdentifier);
}

private InputStream getInputStream(final MultipartFile file) {
try {
userImages.upload(identifier, file.getInputStream());
user.updateImage(new Image(identifier));
userImages.remove(oldIdentifier);
return file.getInputStream();
} catch (IOException e) {
log.error(e.getMessage());
throw new InvalidFileException();
}
}

private String createIdentifier(final String originalName) {
final String uuid = UUID.randomUUID().toString();
return uuid + originalName;
}
}
46 changes: 9 additions & 37 deletions src/main/java/daybyquest/user/infra/S3UserImages.java
Original file line number Diff line number Diff line change
@@ -1,73 +1,45 @@
package daybyquest.user.infra;

import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.DeleteObjectRequest;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import daybyquest.global.error.exception.InvalidFileException;
import daybyquest.global.s3.S3Images;
import daybyquest.image.vo.BaseImageProperties;
import daybyquest.user.domain.UserImages;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class S3UserImages implements UserImages {

private static final String FOLDER_NAME = "USER";
private static final String CATEGORY_NAME = "USER";

private static final long PUBLIC_URL_EXPIRATION = 1000 * 60 * 2;
private static final long PUBLIC_URL_EXPIRATION = 1000 * 60 * 60;

private final AmazonS3 amazonS3;

private final String bucket;
private final S3Images s3Images;

private final BaseImageProperties baseImageProperties;

public S3UserImages(final AmazonS3 amazonS3, @Value("${aws.bucket}") final String bucket,
final BaseImageProperties baseImageProperties) {
this.amazonS3 = amazonS3;
this.bucket = bucket;
public S3UserImages(final S3Images s3Images, final BaseImageProperties baseImageProperties) {
this.s3Images = s3Images;
this.baseImageProperties = baseImageProperties;
}

@Override
public void upload(final String identifier, final InputStream imageStream) {
try {
final String key = FOLDER_NAME + "/" + identifier;
final ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(imageStream.available());
final PutObjectRequest putObjectRequest = new PutObjectRequest(
bucket, key, imageStream, metadata
);
amazonS3.putObject(putObjectRequest);
} catch (IOException e) {
throw new InvalidFileException();
}
s3Images.upload(CATEGORY_NAME, identifier, imageStream);
}

@Override
public void remove(final String identifier) {
if (identifier.equals(baseImageProperties.getUserIdentifier())) {
return;
}
final String key = FOLDER_NAME + "/" + identifier;
final DeleteObjectRequest deleteObjectRequest = new DeleteObjectRequest(bucket, key);
amazonS3.deleteObject(deleteObjectRequest);
s3Images.remove(CATEGORY_NAME, identifier);
}

@Override
public String getPublicUrl(final String identifier) {
final long expirationLong = System.currentTimeMillis() + PUBLIC_URL_EXPIRATION;
final Date expiration = new Date(expirationLong);
final String key = FOLDER_NAME + "/" + identifier;
final GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, key)
.withMethod(HttpMethod.GET)
.withExpiration(expiration);
return amazonS3.generatePresignedUrl(request).toString();
return s3Images.getPublicUrl(CATEGORY_NAME, identifier, expiration);
}
}
15 changes: 14 additions & 1 deletion src/main/java/daybyquest/user/presentation/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import daybyquest.auth.Authorization;
import daybyquest.auth.UserId;
import daybyquest.user.application.DeleteUserImageService;
import daybyquest.user.application.SaveUserService;
import daybyquest.user.application.UpdateUserImageService;
import daybyquest.user.application.UpdateUserInterestService;
Expand All @@ -13,6 +14,7 @@
import daybyquest.user.dto.request.UpdateUserVisibilityRequest;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand All @@ -33,15 +35,19 @@ public class UserController {

private final UpdateUserImageService updateUserImageService;

private final DeleteUserImageService deleteUserImageService;

public UserController(final SaveUserService saveUserService, final UpdateUserService updateUserService,
final UpdateVisibilityService updateVisibilityService,
final UpdateUserInterestService updateUserInterestService,
final UpdateUserImageService updateUserImageService) {
final UpdateUserImageService updateUserImageService,
final DeleteUserImageService deleteUserImageService) {
this.saveUserService = saveUserService;
this.updateUserService = updateUserService;
this.updateVisibilityService = updateVisibilityService;
this.updateUserInterestService = updateUserInterestService;
this.updateUserImageService = updateUserImageService;
this.deleteUserImageService = deleteUserImageService;
}

@PostMapping("/profile")
Expand Down Expand Up @@ -81,4 +87,11 @@ public ResponseEntity<Void> updateUserImage(@UserId final Long loginId,
updateUserImageService.invoke(loginId, multipartFile);
return ResponseEntity.ok().build();
}

@DeleteMapping("/profile/image")
@Authorization
public ResponseEntity<Void> deleteUserImage(@UserId final Long loginId) {
deleteUserImageService.invoke(loginId);
return ResponseEntity.ok().build();
}
}
Loading