diff --git a/build.gradle b/build.gradle index d99b647..4b3af44 100644 --- a/build.gradle +++ b/build.gradle @@ -69,6 +69,11 @@ dependencies { // Email implementation 'org.springframework.boot:spring-boot-starter-mail' + + // aws service + implementation 'com.amazonaws:aws-java-sdk-core:1.12.429' + implementation 'com.amazonaws:aws-java-sdk-s3:1.12.429' + implementation 'software.amazon.awssdk:s3:2.16.83' } ext { diff --git a/src/main/java/yonseigolf/server/apply/controller/ApplicationController.java b/src/main/java/yonseigolf/server/apply/controller/ApplicationController.java index a602eab..1548649 100644 --- a/src/main/java/yonseigolf/server/apply/controller/ApplicationController.java +++ b/src/main/java/yonseigolf/server/apply/controller/ApplicationController.java @@ -1,32 +1,37 @@ package yonseigolf.server.apply.controller; +import net.bytebuddy.utility.RandomString; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import yonseigolf.server.apply.dto.request.*; import yonseigolf.server.apply.dto.response.ApplicationResponse; +import yonseigolf.server.apply.dto.response.ImageResponse; import yonseigolf.server.apply.dto.response.SingleApplicationResult; +import yonseigolf.server.apply.image.ImageService; import yonseigolf.server.apply.service.ApplyPeriodService; import yonseigolf.server.apply.service.ApplyService; import yonseigolf.server.util.CustomResponse; import java.time.LocalDate; -import java.time.LocalDateTime; @Controller public class ApplicationController { private final ApplyService applicationService; private final ApplyPeriodService applyPeriodService; + private final ImageService imageService; @Autowired - public ApplicationController(ApplyService applicationService, ApplyPeriodService applyPeriodService) { + public ApplicationController(ApplyService applicationService, ApplyPeriodService applyPeriodService, ImageService imageService) { this.applicationService = applicationService; this.applyPeriodService = applyPeriodService; + this.imageService = imageService; } @PostMapping("/application") @@ -211,4 +216,18 @@ public ResponseEntity sendFinalFailEmail() { "연세골프 지원서 최종 불합격자 이메일 전송 성공" )); } + + @PostMapping("/apply/forms/image") + public ResponseEntity> uploadImage(@RequestPart("image") MultipartFile image) { + String imageUrl = imageService.uploadImage(image, RandomString.make(10)); + + return ResponseEntity + .ok() + .body(new CustomResponse( + "success", + 200, + "연세골프 지원서 이미지 업로드 성공", + imageUrl + )); + } } diff --git a/src/main/java/yonseigolf/server/apply/dto/response/ImageResponse.java b/src/main/java/yonseigolf/server/apply/dto/response/ImageResponse.java new file mode 100644 index 0000000..556e667 --- /dev/null +++ b/src/main/java/yonseigolf/server/apply/dto/response/ImageResponse.java @@ -0,0 +1,13 @@ +package yonseigolf.server.apply.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class ImageResponse { + + private String image; +} diff --git a/src/main/java/yonseigolf/server/apply/image/ImageService.java b/src/main/java/yonseigolf/server/apply/image/ImageService.java new file mode 100644 index 0000000..30b964d --- /dev/null +++ b/src/main/java/yonseigolf/server/apply/image/ImageService.java @@ -0,0 +1,54 @@ +package yonseigolf.server.apply.image; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +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.IOException; + +@Service +public class ImageService { + + private final S3Client s3Client; + @Value("${AWS_S3_BUCKET}") + private String bucketName; + + @Autowired + public ImageService(S3Client s3Client) { + + this.s3Client = s3Client; + } + + public String uploadImage(MultipartFile file, String randomId) { + + StringBuilder sb = new StringBuilder(); + String fileName = file.getOriginalFilename() + randomId; + String contentType = file.getContentType(); + + // Upload file + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .bucket(bucketName) + .key("store-image/" + fileName) + .acl("public-read") + .contentDisposition("inline") + .contentType(contentType) + .build(); + + String url = sb.append("https://") + .append(bucketName) + .append(".s3.ap-northeast-2.amazonaws.com/store-image/") + .append(fileName) + .toString(); + + try { + s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(file.getInputStream(), file.getSize())); + return url; + } catch (IOException e) { + throw new IllegalStateException("Failed to upload file", e); + } + } +} diff --git a/src/main/java/yonseigolf/server/apply/service/ApplyService.java b/src/main/java/yonseigolf/server/apply/service/ApplyService.java index 13da315..5d098af 100644 --- a/src/main/java/yonseigolf/server/apply/service/ApplyService.java +++ b/src/main/java/yonseigolf/server/apply/service/ApplyService.java @@ -37,6 +37,12 @@ public ApplyService(ApplicationRepository applicationRepository, EmailRepository public void apply(ApplicationRequest request) { applicationRepository.save(Application.of(request)); + emailService.sendEmail(request.getEmail(), + "안녕하세요. 연세골프입니다.\n\n", + request.getName() +"님의 지원서가 정상적으로 제출되었습니다. \n\n" + + "서류 합격 여부는 추후 이메일로 공지될 예정입니다. \n\n" + + "감사합니다." + ); } public void emailAlarm(EmailAlertRequest request) { diff --git a/src/main/java/yonseigolf/server/config/AwsConfig.java b/src/main/java/yonseigolf/server/config/AwsConfig.java new file mode 100644 index 0000000..0b3be8b --- /dev/null +++ b/src/main/java/yonseigolf/server/config/AwsConfig.java @@ -0,0 +1,28 @@ +package yonseigolf.server.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +@Configuration +public class AwsConfig { + + @Value("${AWS_ACCESS_KEY}") + private String awsAccessKey; + @Value("${AWS_SECRET_ACCESS_KEY}") + private String awsSecretKey; + @Value("${AWS_REGION}") + private String region; + + @Bean + public S3Client s3Client() { + return S3Client.builder() + .region(Region.of(region)) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(awsAccessKey, awsSecretKey))) + .build(); + } +} diff --git a/src/test/java/yonseigolf/server/apply/controller/ApplicationControllerTest.java b/src/test/java/yonseigolf/server/apply/controller/ApplicationControllerTest.java index dba8620..e723ecc 100644 --- a/src/test/java/yonseigolf/server/apply/controller/ApplicationControllerTest.java +++ b/src/test/java/yonseigolf/server/apply/controller/ApplicationControllerTest.java @@ -13,6 +13,7 @@ import yonseigolf.server.apply.dto.response.ApplicationResponse; import yonseigolf.server.apply.dto.response.RecruitPeriodResponse; import yonseigolf.server.apply.dto.response.SingleApplicationResult; +import yonseigolf.server.apply.image.ImageService; import yonseigolf.server.apply.service.ApplyPeriodService; import yonseigolf.server.apply.service.ApplyService; import yonseigolf.server.docs.utils.RestDocsSupport; @@ -41,11 +42,13 @@ public class ApplicationControllerTest extends RestDocsSupport { private ApplyService applyService; @Mock private ApplyPeriodService applyPeriodService; + @Mock + private ImageService imageService; @Override protected Object initController() { - return new ApplicationController(applyService, applyPeriodService); + return new ApplicationController(applyService, applyPeriodService, imageService); } @Test