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
Expand Up @@ -19,9 +19,12 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.example.solidconnection.custom.exception.ErrorCode.APPLICATION_NOT_APPROVED;

Expand Down Expand Up @@ -64,12 +67,19 @@ public ApplicationsResponse getApplicantsByUserApplications(String email) {
SiteUser siteUser = siteUserRepository.getByEmail(email);

Application userLatestApplication = applicationRepository.getApplicationBySiteUserAndTerm(siteUser, term);
List<University> userAppliedUniversities = List.of(
userLatestApplication.getFirstChoiceUniversity().getUniversity(),
userLatestApplication.getSecondChoiceUniversity().getUniversity(),
userLatestApplication.getThirdChoiceUniversity().getUniversity()
);

List<University> userAppliedUniversities = Arrays.asList(
Optional.ofNullable(userLatestApplication.getFirstChoiceUniversity())
.map(UniversityInfoForApply::getUniversity)
.orElse(null),
Optional.ofNullable(userLatestApplication.getSecondChoiceUniversity())
.map(UniversityInfoForApply::getUniversity)
.orElse(null),
Optional.ofNullable(userLatestApplication.getThirdChoiceUniversity())
.map(UniversityInfoForApply::getUniversity)
.orElse(null)
).stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());

List<UniversityApplicantsResponse> firstChoiceApplicants = getFirstChoiceApplicants(userAppliedUniversities, siteUser, term);
List<UniversityApplicantsResponse> secondChoiceApplicants = getSecondChoiceApplicants(userAppliedUniversities, siteUser, term);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,13 @@ public class AuthController implements AuthControllerSwagger {
@PostMapping("/kakao")
public ResponseEntity<KakaoOauthResponse> processKakaoOauth(@RequestBody KakaoCodeRequest kakaoCodeRequest) {
KakaoOauthResponse kakaoOauthResponse = signInService.signIn(kakaoCodeRequest);
return ResponseEntity
.ok(kakaoOauthResponse);
return ResponseEntity.ok(kakaoOauthResponse);
}

@PostMapping("/sign-up")
public ResponseEntity<SignUpResponse> signUp(@Valid @RequestBody SignUpRequest signUpRequest) {
SignUpResponse signUpResponseDto = signUpService.signUp(signUpRequest);
return ResponseEntity
.ok(signUpResponseDto);
return ResponseEntity.ok(signUpResponseDto);
}

@PostMapping("/sign-out")
Expand All @@ -57,7 +55,6 @@ public ResponseEntity<Void> quit(Principal principal) {
@PostMapping("/reissue")
public ResponseEntity<ReissueResponse> reissueToken(Principal principal) {
ReissueResponse reissueResponse = authService.reissue(principal.getName());
return ResponseEntity
.ok(reissueResponse);
return ResponseEntity.ok(reissueResponse);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.example.solidconnection.s3;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.example.solidconnection.custom.exception.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

import static com.example.solidconnection.custom.exception.ErrorCode.S3_CLIENT_EXCEPTION;
import static com.example.solidconnection.custom.exception.ErrorCode.S3_SERVICE_EXCEPTION;

@Component
@EnableAsync
@Slf4j
public class FileUploadService {
private final AmazonS3Client amazonS3;

public FileUploadService(AmazonS3Client amazonS3) {
this.amazonS3 = amazonS3;
}

@Async
public void uploadFile(String bucket, String fileName, MultipartFile multipartFile) {
// 메타데이터 생성
String contentType = multipartFile.getContentType();
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(contentType);
metadata.setContentLength(multipartFile.getSize());

try {
amazonS3.putObject(new PutObjectRequest(bucket, fileName, multipartFile.getInputStream(), metadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
log.info("이미지 업로드 정상적 완료 thread: {}", Thread.currentThread().getName());
} catch (AmazonServiceException e) {
log.error("이미지 업로드 중 s3 서비스 예외 발생 : {}", e.getMessage());
throw new CustomException(S3_SERVICE_EXCEPTION);
} catch (SdkClientException | IOException e) {
log.error("이미지 업로드 중 s3 클라이언트 예외 발생 : {}", e.getMessage());
throw new CustomException(S3_CLIENT_EXCEPTION);
}
}
}
19 changes: 15 additions & 4 deletions src/main/java/com/example/solidconnection/s3/S3Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

import com.example.solidconnection.type.ImgType;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.security.Principal;
Expand All @@ -17,6 +15,14 @@
public class S3Controller implements S3ControllerSwagger {

private final S3Service s3Service;
@Value("${cloud.aws.s3.url.default}")
private String s3Default;
@Value("${cloud.aws.s3.url.uploaded}")
private String s3Uploaded;
@Value("${cloud.aws.cloudFront.url.default}")
private String cloudFrontDefault;
@Value("${cloud.aws.cloudFront.url.uploaded}")
private String cloudFrontUploaded;

@PostMapping("/profile/pre")
public ResponseEntity<UploadedFileUrlResponse> uploadPreProfileImage(
Expand Down Expand Up @@ -46,4 +52,9 @@ public ResponseEntity<UploadedFileUrlResponse> uploadLanguageImage(
UploadedFileUrlResponse profileImageUrl = s3Service.uploadFile(imageFile, ImgType.LANGUAGE_TEST);
return ResponseEntity.ok(profileImageUrl);
}

@GetMapping("/s3-url-prefix")
public ResponseEntity<urlPrefixResponse> getS3UrlPrefix() {
return ResponseEntity.ok(new urlPrefixResponse(s3Default, s3Uploaded, cloudFrontDefault, cloudFrontUploaded));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,21 @@ public interface S3ControllerSwagger {
}
)
ResponseEntity<UploadedFileUrlResponse> uploadLanguageImage(@RequestParam("file") MultipartFile imageFile);

@SecurityRequirements
@SecurityRequirement(name = ACCESS_TOKEN)
@Operation(
summary = "S3 url prefix 확인",
responses = {
@ApiResponse(
responseCode = "200",
description = "S3 url prefix 반환",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = urlPrefixResponse.class)
)
)
}
)
ResponseEntity<urlPrefixResponse> getS3UrlPrefix();
}
79 changes: 20 additions & 59 deletions src/main/java/com/example/solidconnection/s3/S3Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
import com.amazonaws.AmazonServiceException;
import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.DeleteObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.example.solidconnection.custom.exception.CustomException;
import com.example.solidconnection.siteuser.domain.SiteUser;
import com.example.solidconnection.siteuser.repository.SiteUserRepository;
Expand All @@ -15,10 +12,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;

import static com.example.solidconnection.custom.exception.ErrorCode.FILE_NOT_EXIST;
Expand All @@ -34,8 +31,11 @@ public class S3Service {
private static final Logger log = LoggerFactory.getLogger(S3Service.class);
private final AmazonS3Client amazonS3;
private final SiteUserRepository siteUserRepository;
private final FileUploadService fileUploadService;
private final ThreadPoolTaskExecutor asyncExecutor;
@Value("${cloud.aws.s3.bucket}")
private String bucket;
private final long MAX_FILE_SIZE_MB = 1024 * 1024 * 3;

/*
* 파일을 S3에 업로드한다.
Expand All @@ -44,66 +44,35 @@ public class S3Service {
* - 파일에 대한 메타 데이터를 생성한다.
* - 임의의 랜덤한 문자열로 파일 이름을 생성한다.
* - S3에 파일을 업로드한다.
* - 3mb 이상의 파일은 /origin/ 경로로 업로드하여 lambda 함수로 리사이징 진행한다.
* - 3mb 미만의 파일은 바로 업로드한다.
* */
public UploadedFileUrlResponse uploadFile(MultipartFile multipartFile, ImgType imageFile) {
// 파일 검증
validateImgFile(multipartFile);

// 메타데이터 생성
String contentType = multipartFile.getContentType();
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(contentType);
metadata.setContentLength(multipartFile.getSize());

// 파일 이름 생성
UUID randomUUID = UUID.randomUUID();
String fileName = imageFile.getType() + "/" + randomUUID;

try {
amazonS3.putObject(new PutObjectRequest(bucket, fileName, multipartFile.getInputStream(), metadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (AmazonServiceException e) {
log.error("이미지 업로드 중 s3 서비스 예외 발생 : {}", e.getMessage());
throw new CustomException(S3_SERVICE_EXCEPTION);
} catch (SdkClientException | IOException e) {
log.error("이미지 업로드 중 s3 클라이언트 예외 발생 : {}", e.getMessage());
throw new CustomException(S3_CLIENT_EXCEPTION);
// 파일업로드 비동기로 진행
if (multipartFile.getSize() >= MAX_FILE_SIZE_MB) {
asyncExecutor.submit(() -> {
fileUploadService.uploadFile(bucket, "origin/" + fileName, multipartFile);
});
} else {
asyncExecutor.submit(() -> {
fileUploadService.uploadFile(bucket, fileName, multipartFile);
});
}

return new UploadedFileUrlResponse(amazonS3.getUrl(bucket, fileName).toString());
return new UploadedFileUrlResponse(fileName);
}

public List<UploadedFileUrlResponse> uploadFiles(List<MultipartFile> multipartFile, ImgType imageFile) {

List<UploadedFileUrlResponse> uploadedFileUrlResponseList = new ArrayList<>();

for (MultipartFile file : multipartFile) {
// 파일 검증
validateImgFile(file);

// 메타데이터 생성
String contentType = file.getContentType();
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(contentType);
metadata.setContentLength(file.getSize());

// 파일 이름 생성
UUID randomUUID = UUID.randomUUID();
String fileName = imageFile.getType() + "/" + randomUUID;

try {
amazonS3.putObject(new PutObjectRequest(bucket, fileName, file.getInputStream(), metadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (AmazonServiceException e) {
log.error("이미지 업로드 중 s3 서비스 예외 발생 : {}", e.getMessage());
throw new CustomException(S3_SERVICE_EXCEPTION);
} catch (SdkClientException | IOException e) {
log.error("이미지 업로드 중 s3 클라이언트 예외 발생 : {}", e.getMessage());
throw new CustomException(S3_CLIENT_EXCEPTION);
}
uploadedFileUrlResponseList.add(new UploadedFileUrlResponse(amazonS3.getUrl(bucket, fileName).toString()));
UploadedFileUrlResponse uploadedFileUrlResponse = uploadFile(file, imageFile);
uploadedFileUrlResponseList.add(uploadedFileUrlResponse);
}

return uploadedFileUrlResponseList;
}

Expand Down Expand Up @@ -140,8 +109,7 @@ public void deleteExProfile(String email) {
}

public void deletePostImage(String url) {
String key = getPostImageUrl(url);
deleteFile(key);
deleteFile(url);
}

private void deleteFile(String fileName) {
Expand All @@ -158,13 +126,6 @@ private void deleteFile(String fileName) {

private String getExProfileImageUrl(String email) {
SiteUser siteUser = siteUserRepository.getByEmail(email);
String fileName = siteUser.getProfileImageUrl();
int domainStartIndex = fileName.indexOf(".com");
return fileName.substring(domainStartIndex + 5);
}

private String getPostImageUrl(String url) {
int domainStartIndex = url.indexOf(".com");
return url.substring(domainStartIndex + 5);
return siteUser.getProfileImageUrl();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.solidconnection.s3;

public record urlPrefixResponse(
String s3Default,
String s3Uploaded,
String cloudFrontDefault,
String cloudFrontUploaded
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public record MyPageResponse(
@Schema(description = "생년월일", example = "1990-01-01")
String birth,

@Schema(description = "이메일", example = "example@solid-conenct.net")
String email,

@Schema(description = "좋아요 누른 게시물 수", example = "0")
int likedPostCount,

Expand All @@ -34,6 +37,7 @@ public static MyPageResponse of(SiteUser siteUser, int likedUniversityCount) {
siteUser.getProfileImageUrl(),
siteUser.getRole(),
siteUser.getBirth(),
siteUser.getEmail(),
0, // TODO: 커뮤니티 기능 생기면 업데이트 필요
0, // TODO: 멘토 기능 생기면 업데이트 필요
likedUniversityCount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private void validateProfileImage(MultipartFile imageFile) {
}

private boolean isDefaultProfileImage(String profileImageUrl) {
String prefix = "https://solid-connection-uploaded.s3.ap-northeast-2.amazonaws.com/profile/";
String prefix = "profile/";
return profileImageUrl == null || !profileImageUrl.startsWith(prefix);
}

Expand Down
Loading