Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package leets.bookmark.domain.bookmark.application.dto.request;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import leets.bookmark.domain.bookmark.domain.entity.enums.Platform;
import leets.bookmark.domain.notification.application.dto.request.NotificationSaveRequest;
import leets.bookmark.domain.file.application.dto.request.FileSaveRequest;
import leets.bookmark.domain.user.domain.entity.User;
import lombok.Builder;


Expand All @@ -17,7 +14,7 @@ public record BookmarkSaveRequest(
@NotBlank String title,
@NotBlank String url,
String memo,
@NotNull @Valid FileSaveRequest file,
@NotBlank String thumbnailUrl,
NotificationSaveRequest notification,
@NotNull Platform platform,
@NotNull Long categoryId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public record BookmarkUpdateRequest(
String title,
String url,
String memo,
FileUpdateRequest file,
String thumbnailUrl,
NotificationUpdateRequest notification,
Platform platform,
Long categoryId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ public void save(Long userId, BookmarkSaveRequest request) {
Category category = categoryGetService.findById(request.categoryId());
Bookmark bookmark = bookmarkSaveService.save(request, user, category);

if (request.file() != null) {
fileUseCase.saveFile(user, bookmark, request.file());
if (request.thumbnailUrl() != null) {
fileUseCase.saveThumbnailFile(user, bookmark, request.thumbnailUrl());
}
}

Expand All @@ -163,10 +163,12 @@ public void update(Long userId, Long bookmarkId, BookmarkUpdateRequest request,

if (request.title() != null && !request.title().trim().isEmpty()) {
bookmark.updateTitle(request.title());
updated = true;
}

if (request.file() != null) {
fileUseCase.updateFile(user, bookmark, request.file());
if (request.thumbnailUrl() != null) {
fileUseCase.updateThumbnailImage(user, bookmark, request.thumbnailUrl());
updated = true;
}

if (request.platform() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ public class BookmarkUpdateService {

@Transactional
public void update(Bookmark bookmark, BookmarkUpdateRequest request) {
FileUpdateRequest fileRequest = request.file();
bookmark.updateBookmark(
request.title(),
request.memo()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ public enum FileErrorCode implements ErrorCode {

FILE_NOT_FOUND_EXCEPTION(404,"해당하는 파일을 찾을 수 없습니다."),
INVALID_FILE_EXTENSION(400, "올바르지 않은 파일 확장자입니다."),
FILE_OWNER_MISMATCH_EXCEPTION(403, "파일 소유자만 접근 가능합니다");
FILE_OWNER_MISMATCH_EXCEPTION(403, "파일 소유자만 접근 가능합니다."),
S3_UPLOAD_EXCEPTION(500, "파일 업로드에 실패하였습니다.");

private final int errorCode;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package leets.bookmark.domain.file.application.exception;

import leets.bookmark.global.common.exception.BusinessException;

public class S3UploadException extends BusinessException {
public S3UploadException(){
super(FileErrorCode.S3_UPLOAD_EXCEPTION);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ public File toFile(User user, Bookmark bookmark, FileSaveRequest fileSaveRequest
.build();
}

public File toThumbnailFile(User user, Bookmark bookmark, String fileName,
String s3UrlResponse, FileType type) {
return File.builder()
.user(user)
.bookmark(bookmark)
.fileName(fileName)
.fileUrl(s3UrlResponse)
.fileType(type)
.build();
}

public FileResponse toFileResponse(File file){
return FileResponse.builder()
.fileUrl(file.getFileUrl())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
public class FileUseCase {

private final PreSignedService preSignedService;
private final S3UploadService s3UploadService;
private final FileSaveService fileSaveService;
private final FileGetService fileGetService;
private final FileUpdateService fileUpdateService;
Expand All @@ -51,6 +52,17 @@ public void saveFile(User user, Bookmark bookmark, @Valid FileSaveRequest fileSa
fileSaveService.save(file);
}

@Transactional
public void saveThumbnailFile(User user, Bookmark bookmark, String thumbnailUrl) {
String fileName = extractFileNameWithExtension(thumbnailUrl);
FileType type = getValidatedFileType(fileName);

String s3UrlResponse = s3UploadService.upload(thumbnailUrl);

File file = fileMapper.toThumbnailFile(user, bookmark, fileName, s3UrlResponse, type);
fileSaveService.save(file);
}

@Transactional(readOnly = true)
public FileResponse getFile(User user, Bookmark bookmark) {
File file = fileGetService.findByBookmarkId(bookmark.getId());
Expand All @@ -67,6 +79,18 @@ public void updateFile(User user, Bookmark bookmark, @Valid FileUpdateRequest fi
fileUpdateService.update(file, fileUpdateRequest, getValidatedFileType(fileUpdateRequest.fileName()));
}

public void updateThumbnailImage(User user, Bookmark bookmark, String thumbnailUrl) {
File file = fileGetService.findByBookmarkId(bookmark.getId());
validateFileOwner(user, file);

String fileName = extractFileNameWithExtension(thumbnailUrl);
FileType type = getValidatedFileType(fileName);

String s3UrlResponse = s3UploadService.upload(thumbnailUrl);

fileUpdateService.updateThumbnailImage(file, fileName, s3UrlResponse, type);
}

@Transactional
public void deleteFile(User user, Bookmark bookmark) {
File file = fileGetService.findByBookmarkId(bookmark.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public enum FileType {
PPTX(".pptx"),
PPT(".ppt"),
TXT(".txt"),
WEBP(".webp");
WEBP(".webp"),
HEIC(".heic");

private final String extension;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ public class FileUpdateService {
public void update(File file, FileUpdateRequest fileUpdateRequest, FileType fileType) {
file.updateFile(fileUpdateRequest.fileName(), fileUpdateRequest.fileUrl(), fileType);
}

public void updateThumbnailImage(File file, String fileName, String s3Url, FileType type) {
file.updateFile(fileName, s3Url, type);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package leets.bookmark.domain.file.domain.service;

import leets.bookmark.domain.file.application.exception.S3UploadException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;

import java.io.InputStream;
import java.net.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@RequiredArgsConstructor
@Service
public class S3UploadService {

@Value("${cloud.aws.s3.bucket}")
private String bucket;

@Value("${cloud.aws.region.static}")
private String region;

private final S3Client s3Client;

private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

public String upload(String fileUrl) {
try {
URL url = URI.create(fileUrl).toURL();
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");

try (InputStream inputStream = connection.getInputStream()) {

String fileName = generateUrl(fileUrl);

PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucket)
.key(fileName)
.contentType(connection.getContentType())
.build();

s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(inputStream, connection.getContentLengthLong()));

return "https://" + bucket + ".s3." + region + ".amazonaws.com/" + fileName;
}

} catch (Exception e){
throw new S3UploadException();
}
}

private String generateUrl(String fileName) {
String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
return LocalDateTime.now().format(FORMATTER) + UUID.randomUUID().toString() + "." + extension;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ public CommonResponse<PresignedUrlResponse> getPresignedUrl(@RequestParam String
PresignedUrlResponse response = fileUseCase.getPreSignedUrl(fileName);
return CommonResponse.createSuccess(FileResponseCode.PRE_SIGNED_URL_SUCCESS.getMessage(), response);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
@Getter
@RequiredArgsConstructor
public enum FileResponseCode {
PRE_SIGNED_URL_SUCCESS("파일 업로드 URL이 생성되었습니다.");
PRE_SIGNED_URL_SUCCESS("파일 업로드 URL이 생성되었습니다."),
S3_UPLOAD_SUCCESS("S3 업로드에 성공했습니다.");

private final String message;
}
12 changes: 12 additions & 0 deletions src/main/java/leets/bookmark/global/config/AmazonS3Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;

@Configuration
Expand All @@ -34,4 +35,15 @@ public S3Presigner s3Presigner(){
.credentialsProvider(credentialsProvider)
.build();
}

@Bean
public S3Client s3Client() {
AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
AwsBasicCredentials.create(accessKey, secretKey)
);
return S3Client.builder()
.region(Region.of(region))
.credentialsProvider(credentialsProvider)
.build();
}
}