From e76e067ce1930ea7e4dfd1e659a8b0e67c7a06e8 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:28:03 +0900 Subject: [PATCH] =?UTF-8?q?[feat/#72]=20OCR=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 + .../api/controller/ocr/OcrController.java | 37 +++++ .../ocr/dto/response/PostOcrRes.java | 23 +++ .../api/controller/use/UseController.java | 1 + .../api/service/auth/AuthServiceImpl.java | 2 +- .../social/kakao/KakaoLoginServiceImpl.java | 2 + .../service/ocr/OcrService/OcrService.java | 10 ++ .../ocr/OcrService/OcrServiceImpl.java | 85 +++++++++++ .../api/service/use/UseServiceImpl.java | 3 - .../common/code/status/ErrorStatus.java | 3 + .../common/code/status/SuccessStatus.java | 3 +- .../academy/common/config/S3Config.java | 32 +++++ .../academy/utils/clova/ClovaOCR.java | 132 ++++++++++++++++++ .../academy/utils/s3/S3Provider.java | 72 ++++++++++ .../academy/utils/s3/dto/S3UploadRequest.java | 17 +++ src/main/resources/application.yml | 19 +++ 16 files changed, 442 insertions(+), 5 deletions(-) create mode 100644 src/main/java/carbonneutral/academy/api/controller/ocr/OcrController.java create mode 100644 src/main/java/carbonneutral/academy/api/controller/ocr/dto/response/PostOcrRes.java create mode 100644 src/main/java/carbonneutral/academy/api/service/ocr/OcrService/OcrService.java create mode 100644 src/main/java/carbonneutral/academy/api/service/ocr/OcrService/OcrServiceImpl.java create mode 100644 src/main/java/carbonneutral/academy/common/config/S3Config.java create mode 100644 src/main/java/carbonneutral/academy/utils/clova/ClovaOCR.java create mode 100644 src/main/java/carbonneutral/academy/utils/s3/S3Provider.java create mode 100644 src/main/java/carbonneutral/academy/utils/s3/dto/S3UploadRequest.java diff --git a/build.gradle b/build.gradle index 0d3aad3..c7a85ff 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,12 @@ dependencies { annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" + //s3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + + implementation 'org.json:json:20210307' + + } tasks.named('test') { diff --git a/src/main/java/carbonneutral/academy/api/controller/ocr/OcrController.java b/src/main/java/carbonneutral/academy/api/controller/ocr/OcrController.java new file mode 100644 index 0000000..55d1266 --- /dev/null +++ b/src/main/java/carbonneutral/academy/api/controller/ocr/OcrController.java @@ -0,0 +1,37 @@ +package carbonneutral.academy.api.controller.ocr; + + +import carbonneutral.academy.api.controller.ocr.dto.response.PostOcrRes; +import carbonneutral.academy.api.service.ocr.OcrService.OcrService; +import carbonneutral.academy.common.BaseResponse; +import carbonneutral.academy.domain.user.User; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +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.multipart.MultipartFile; + +import static carbonneutral.academy.common.code.status.SuccessStatus.OCR_OK; + +@Slf4j +@Tag(name = "ocr controller", description = "ocr 관련 API") +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/v1/ocr") +public class OcrController { + + private final OcrService ocrService; + + @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "OCR 이미지 인식 API",description = "OCR 이미지를 인식합니다.") + public BaseResponse ocrImage(@AuthenticationPrincipal User user + , @RequestParam("receipt") MultipartFile receipt) { + return BaseResponse.of(OCR_OK, ocrService.ocrImage(user, receipt)); + } +} diff --git a/src/main/java/carbonneutral/academy/api/controller/ocr/dto/response/PostOcrRes.java b/src/main/java/carbonneutral/academy/api/controller/ocr/dto/response/PostOcrRes.java new file mode 100644 index 0000000..963e541 --- /dev/null +++ b/src/main/java/carbonneutral/academy/api/controller/ocr/dto/response/PostOcrRes.java @@ -0,0 +1,23 @@ +package carbonneutral.academy.api.controller.ocr.dto.response; + +import carbonneutral.academy.domain.use.enums.UseStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@Builder +@AllArgsConstructor +public class PostOcrRes { + + private int useLocationId; + private String useLocationName; + private String useLocationAddress; + private String useLocationImageUrl; + private String useTime; + private int currentPoint; + private int acquiredPoint; + private int userId; +} diff --git a/src/main/java/carbonneutral/academy/api/controller/use/UseController.java b/src/main/java/carbonneutral/academy/api/controller/use/UseController.java index 3b62494..0085d6b 100644 --- a/src/main/java/carbonneutral/academy/api/controller/use/UseController.java +++ b/src/main/java/carbonneutral/academy/api/controller/use/UseController.java @@ -59,4 +59,5 @@ BaseResponse returnMultipleTimeContainers(@AuthenticationPrincip @Validated @RequestBody PatchReturnReq patchReturnReq, @PathVariable("useAt") String useAt) { return BaseResponse.of(RETURN_SAVE_OK, useService.returnMultipleTimeContainers(user, patchReturnReq, useAt)); } + } diff --git a/src/main/java/carbonneutral/academy/api/service/auth/AuthServiceImpl.java b/src/main/java/carbonneutral/academy/api/service/auth/AuthServiceImpl.java index f6b3d22..0392926 100644 --- a/src/main/java/carbonneutral/academy/api/service/auth/AuthServiceImpl.java +++ b/src/main/java/carbonneutral/academy/api/service/auth/AuthServiceImpl.java @@ -45,7 +45,7 @@ public class AuthServiceImpl implements AuthService { public PostSocialRes socialLogin(String code, SocialType socialType) { switch (socialType){ case KAKAO: { - GetKakaoRes getKakaoRes = kakaoLoginService.getUserInfo(code); + GetKakaoRes getKakaoRes = kakaoLoginService.getUserInfo(kakaoLoginService.getAccessToken(code)); boolean isRegistered = userJpaRepository.existsByUsernameAndSocialTypeAndState(getKakaoRes.getId(), SocialType.KAKAO, ACTIVE); diff --git a/src/main/java/carbonneutral/academy/api/service/auth/social/kakao/KakaoLoginServiceImpl.java b/src/main/java/carbonneutral/academy/api/service/auth/social/kakao/KakaoLoginServiceImpl.java index a553d26..afdd625 100644 --- a/src/main/java/carbonneutral/academy/api/service/auth/social/kakao/KakaoLoginServiceImpl.java +++ b/src/main/java/carbonneutral/academy/api/service/auth/social/kakao/KakaoLoginServiceImpl.java @@ -27,6 +27,7 @@ public class KakaoLoginServiceImpl implements KakaoLoginService { + @Value("${spring.security.oauth2.client.provider.kakao.token-uri}") private String KAKAO_TOKEN_URL; @@ -68,6 +69,7 @@ public String getAccessToken(String authorizationCode) { } } + @Override public GetKakaoRes getUserInfo(String accessToken){ RestTemplate restTemplate = new RestTemplate(); diff --git a/src/main/java/carbonneutral/academy/api/service/ocr/OcrService/OcrService.java b/src/main/java/carbonneutral/academy/api/service/ocr/OcrService/OcrService.java new file mode 100644 index 0000000..0da4a84 --- /dev/null +++ b/src/main/java/carbonneutral/academy/api/service/ocr/OcrService/OcrService.java @@ -0,0 +1,10 @@ +package carbonneutral.academy.api.service.ocr.OcrService; + +import carbonneutral.academy.api.controller.ocr.dto.response.PostOcrRes; +import carbonneutral.academy.domain.user.User; +import org.springframework.web.multipart.MultipartFile; + +public interface OcrService { + + PostOcrRes ocrImage(User user, MultipartFile receipt); +} diff --git a/src/main/java/carbonneutral/academy/api/service/ocr/OcrService/OcrServiceImpl.java b/src/main/java/carbonneutral/academy/api/service/ocr/OcrService/OcrServiceImpl.java new file mode 100644 index 0000000..c702e92 --- /dev/null +++ b/src/main/java/carbonneutral/academy/api/service/ocr/OcrService/OcrServiceImpl.java @@ -0,0 +1,85 @@ +package carbonneutral.academy.api.service.ocr.OcrService; + +import carbonneutral.academy.api.controller.ocr.dto.response.PostOcrRes; +import carbonneutral.academy.api.converter.time.TimeConverter; +import carbonneutral.academy.api.converter.use.UseConverter; +import carbonneutral.academy.api.service.use.UseService; +import carbonneutral.academy.api.service.user.UserService; +import carbonneutral.academy.common.exceptions.BaseException; +import carbonneutral.academy.domain.location.Location; +import carbonneutral.academy.domain.location.repository.LocationJpaRepository; +import carbonneutral.academy.domain.point.Point; +import carbonneutral.academy.domain.point.repository.PointJpaRepository; +import carbonneutral.academy.domain.use.Use; +import carbonneutral.academy.domain.use.enums.UseStatus; +import carbonneutral.academy.domain.use.repository.UseJpaRepository; +import carbonneutral.academy.domain.use_statistics.repository.UseStatisticsJpaRepository; +import carbonneutral.academy.domain.user.User; +import carbonneutral.academy.utils.clova.ClovaOCR; +import carbonneutral.academy.utils.s3.S3Provider; +import carbonneutral.academy.utils.s3.dto.S3UploadRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.time.LocalDateTime; + +import static carbonneutral.academy.common.BaseEntity.State.ACTIVE; +import static carbonneutral.academy.common.code.status.ErrorStatus.*; + +@Service +@RequiredArgsConstructor +@Slf4j +@Transactional +public class OcrServiceImpl implements OcrService { + + private final S3Provider s3Provider; + private final ClovaOCR clovaOCR; + private final UseJpaRepository useJpaRepository; + private final LocationJpaRepository locationJpaRepository; + private final UseStatisticsJpaRepository useStatisticsJpaRepository; + private final PointJpaRepository pointJpaRepository; + + @Override + public PostOcrRes ocrImage(User user, MultipartFile receipt) { + String receiptUrl = s3Provider.multipartFileUpload(receipt, S3UploadRequest.builder().userId(user.getId()).dirName("receipt").build()); + log.info("receiptUrl : {}", receiptUrl); + String result = clovaOCR.OCRParse(receiptUrl); + //결과에 컵 할인 없으면 예외 던지기 + if (!result.contains("컵 할인")) { + throw new BaseException(NOT_FIND_CUP_DISCOUNT); + } + Location location = locationJpaRepository.findByIdAndState(3, ACTIVE) + .orElseThrow(() -> new BaseException(NOT_FIND_LOCATION)); + Use use = Use.builder() + .useAt(LocalDateTime.now()) + .point(100) + .returnTime(LocalDateTime.now()) + .multiUseContainerId(5) + .rentalLocation(location) + .returnLocation(location) + .status(UseStatus.RETURNED) + .user(user) + .build(); + useJpaRepository.save(use); + Point point = pointJpaRepository.findByUserId(user.getId()).orElseThrow(() -> new BaseException(NOT_FIND_POINT)); + point.addPoint(100); + useStatisticsJpaRepository.findById(user.getId()).orElseThrow(() -> new BaseException(NOT_FIND_USE_STATISTICS)).addTotalUseCount(); + useStatisticsJpaRepository.findById(user.getId()).orElseThrow(() -> new BaseException(NOT_FIND_USE_STATISTICS)).addTotalReturnCount(); + return PostOcrRes.builder() + .useTime(TimeConverter.toFormattedDate(use.getUseAt())) + .currentPoint(point.getAccumulatedPoint() - point.getUtilizedPoint()) + .acquiredPoint(use.getPoint()) + .useLocationId(location.getId()) + .useLocationName(location.getName()) + .useLocationAddress(location.getAddress()) + .useLocationImageUrl(location.getImageUrl()) + .userId(user.getId()) + .build(); + } + + + +} diff --git a/src/main/java/carbonneutral/academy/api/service/use/UseServiceImpl.java b/src/main/java/carbonneutral/academy/api/service/use/UseServiceImpl.java index b1536c8..249531f 100644 --- a/src/main/java/carbonneutral/academy/api/service/use/UseServiceImpl.java +++ b/src/main/java/carbonneutral/academy/api/service/use/UseServiceImpl.java @@ -124,12 +124,9 @@ public PatchReturnRes returnMultipleTimeContainers(User user, PatchReturnReq pat log.info("use : {}", use.getUseAt()); Location returnLocation = locationJpaRepository.findByIdAndState(patchReturnReq.getReturnLocationId(), ACTIVE) .orElseThrow(() -> new BaseException(NOT_FIND_LOCATION)); - log.info("returnLocation : {}", returnLocation.getId()); - log.info("returnLocation : {} {}", returnLocation.getLocationType(), returnLocation.isReturned()); if(!(returnLocation.isReturned()) && !(returnLocation.getLocationType().equals(LocationType.RETURN))) { throw new BaseException(NOT_RETURN_LOCATION); } - log.info("returnLocation여기니? : {}", returnLocation.getId()); if(!locationContainerJpaRepository.findByLocation_Id(returnLocation.getId()) .stream() .map(locationContainer -> locationContainer.getMultiUseContainer().getId()) diff --git a/src/main/java/carbonneutral/academy/common/code/status/ErrorStatus.java b/src/main/java/carbonneutral/academy/common/code/status/ErrorStatus.java index 9d35161..8ef224b 100644 --- a/src/main/java/carbonneutral/academy/common/code/status/ErrorStatus.java +++ b/src/main/java/carbonneutral/academy/common/code/status/ErrorStatus.java @@ -50,6 +50,9 @@ public enum ErrorStatus implements BaseErrorCode { INVALID_PAGE(HttpStatus.BAD_REQUEST, "PAGE4000", "페이지는 0 이상이어야 합니다."), //사이즈는 무조건 10 INVALID_SIZE_10(HttpStatus.BAD_REQUEST, "PAGE4001", "사이즈는 10이어야 합니다."), + FILE_CONVERT(HttpStatus.BAD_REQUEST, "FILE4001", "파일 변환 실패."), + S3_UPLOAD(HttpStatus.BAD_REQUEST, "S3UPLOAD4001", "S3 파일 업로드 실패."), + NOT_FIND_CUP_DISCOUNT(HttpStatus.BAD_REQUEST, "CUP_DISCOUNT4000", "텀블러를 사용한 할인 내역이 없습니다."), /** diff --git a/src/main/java/carbonneutral/academy/common/code/status/SuccessStatus.java b/src/main/java/carbonneutral/academy/common/code/status/SuccessStatus.java index 8efbc2a..3cafd3a 100644 --- a/src/main/java/carbonneutral/academy/common/code/status/SuccessStatus.java +++ b/src/main/java/carbonneutral/academy/common/code/status/SuccessStatus.java @@ -26,7 +26,8 @@ public enum SuccessStatus implements BaseCode { IN_USE_OK(HttpStatus.OK, "USE2002", "이용중인 다회용기 단일 조회 성공" ), RETURN_SAVE_OK(HttpStatus.CREATED, "USE2003", "반납 성공"), GET_POINT_OK(HttpStatus.OK, "POINT2000", "포인트 조회 성공"), - LOCATION_OK(HttpStatus.OK, "LOCATION2000", "장소 조회 성공"); + LOCATION_OK(HttpStatus.OK, "LOCATION2000", "장소 조회 성공"), + OCR_OK(HttpStatus.OK, "OCR2000", "텀블러 이용 성공"); private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/carbonneutral/academy/common/config/S3Config.java b/src/main/java/carbonneutral/academy/common/config/S3Config.java new file mode 100644 index 0000000..a48b31e --- /dev/null +++ b/src/main/java/carbonneutral/academy/common/config/S3Config.java @@ -0,0 +1,32 @@ +package carbonneutral.academy.common.config; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class S3Config { + @Value("${cloud.aws.region.static}") + private String region; + + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + + @Bean + public AmazonS3Client amazonS3Client() { + BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey); + return (AmazonS3Client) AmazonS3ClientBuilder + .standard() + .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) + .withRegion(region) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/carbonneutral/academy/utils/clova/ClovaOCR.java b/src/main/java/carbonneutral/academy/utils/clova/ClovaOCR.java new file mode 100644 index 0000000..160ed19 --- /dev/null +++ b/src/main/java/carbonneutral/academy/utils/clova/ClovaOCR.java @@ -0,0 +1,132 @@ +package carbonneutral.academy.utils.clova; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +@Component +public class ClovaOCR { + + @Value("${naver.service.url}") + private String API_URL; + + @Value("${naver.service.secretKey}") + private String SECRET_KEY; + + public String OCRParse(String imageUrl){ + try { + URL url = new URL(API_URL); + HttpURLConnection connection = createRequestHeader(url); + createRequestBody(connection, imageUrl); + + StringBuilder response = getResponse(connection); + + // 응답 로그 출력 + System.out.println("API Response: " + response.toString()); + + String result = parseResponseData(response.toString()); + + return result; + } catch(Exception e) { + e.printStackTrace(); + return null; + } + } + + private HttpURLConnection createRequestHeader(URL url) throws IOException { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + connection.setReadTimeout(5000); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json;"); + connection.setRequestProperty("X-OCR-SECRET", SECRET_KEY); + + return connection; + } + + private void createRequestBody(HttpURLConnection connection, String imageUrl) throws IOException { + JSONObject image = new JSONObject(); + + image.put("format", "png"); + image.put("name", "requestImage"); + image.put("url", imageUrl); + + JSONArray images = new JSONArray(); + images.put(image); + + JSONObject jsonRequest = new JSONObject(); + jsonRequest.put("version", "V1"); + jsonRequest.put("requestId", UUID.randomUUID().toString()); + jsonRequest.put("timestamp", System.currentTimeMillis()); + jsonRequest.put("lang", "ko"); + jsonRequest.put("resultType", "string"); + jsonRequest.put("images", images); + + connection.connect(); + DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream()); + dataOutputStream.write(jsonRequest.toString().getBytes(StandardCharsets.UTF_8)); + dataOutputStream.flush(); + dataOutputStream.close(); + } + + private BufferedReader checkResponse(HttpURLConnection connection) throws IOException { + int responseCode = connection.getResponseCode(); + BufferedReader bufferedReader; + + if (responseCode == 200) { + bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + } else { + bufferedReader = new BufferedReader(new InputStreamReader(connection.getErrorStream())); + } + + return bufferedReader; + } + + private StringBuilder getResponse(HttpURLConnection connection) throws IOException { + BufferedReader bufferedReader = checkResponse(connection); + + String str; + StringBuilder response = new StringBuilder(); + + while ((str = bufferedReader.readLine()) != null) { + response.append(str); + } + bufferedReader.close(); + + return response; + } + + private String parseResponseData(String response) { + JSONObject jsonResponse = new JSONObject(response); + JSONArray parsedImages = jsonResponse.getJSONArray("images"); + StringBuilder result = new StringBuilder(); + + if (parsedImages != null && parsedImages.length() > 0) { + JSONObject parsedImage = parsedImages.getJSONObject(0); + JSONArray parsedText = parsedImage.getJSONArray("fields"); + + for (int i = 0; i < parsedText.length(); i++) { + JSONObject text = parsedText.getJSONObject(i); + result.append(text.getString("inferText")).append(" "); + } + } + + // 파싱된 결과 로그 출력 + System.out.println("Parsed Result: " + result.toString()); + + return result.toString(); + } +} diff --git a/src/main/java/carbonneutral/academy/utils/s3/S3Provider.java b/src/main/java/carbonneutral/academy/utils/s3/S3Provider.java new file mode 100644 index 0000000..3371e27 --- /dev/null +++ b/src/main/java/carbonneutral/academy/utils/s3/S3Provider.java @@ -0,0 +1,72 @@ +package carbonneutral.academy.utils.s3; + +import carbonneutral.academy.common.exceptions.BaseException; +import carbonneutral.academy.utils.s3.dto.S3UploadRequest; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.transfer.TransferManager; +import com.amazonaws.services.s3.transfer.TransferManagerBuilder; +import com.amazonaws.services.s3.transfer.Upload; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; + +import static carbonneutral.academy.common.code.status.ErrorStatus.FILE_CONVERT; +import static carbonneutral.academy.common.code.status.ErrorStatus.S3_UPLOAD; + + +@Slf4j +@RequiredArgsConstructor +@Component +public class S3Provider { + private final AmazonS3 amazonS3Client; + private TransferManager transferManager; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + @PostConstruct + public void init() { + this.transferManager = TransferManagerBuilder.standard() + .withS3Client(amazonS3Client) + .build(); + } + + @PreDestroy + public void shutdown() { + if (this.transferManager != null) { + this.transferManager.shutdownNow(); + } + } + + public String multipartFileUpload(MultipartFile file, S3UploadRequest request) { + String fileName = request.getUserId() + "/" + request.getDirName() + "/" + UUID.randomUUID() + "_" + file.getOriginalFilename(); + + try { + InputStream is = file.getInputStream(); + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentType(file.getContentType()); + objectMetadata.setContentLength(file.getSize()); + Upload upload = transferManager.upload(bucket, fileName, is, objectMetadata); + try { + upload.waitForCompletion(); + } catch (InterruptedException e) { + log.error("S3 multipartFileUpload error", e); + throw new BaseException(S3_UPLOAD); + } + } catch (IOException e) { + log.error("File convert error", e); + throw new BaseException(FILE_CONVERT); + } + return amazonS3Client.getUrl(bucket, fileName).toString(); + } +} \ No newline at end of file diff --git a/src/main/java/carbonneutral/academy/utils/s3/dto/S3UploadRequest.java b/src/main/java/carbonneutral/academy/utils/s3/dto/S3UploadRequest.java new file mode 100644 index 0000000..bbb0e5a --- /dev/null +++ b/src/main/java/carbonneutral/academy/utils/s3/dto/S3UploadRequest.java @@ -0,0 +1,17 @@ +package carbonneutral.academy.utils.s3.dto; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data +@AllArgsConstructor +@Builder +public class S3UploadRequest { + + private int userId; + private String dirName; +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3b3dea7..6fb2648 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -14,3 +14,22 @@ jwt: refresh-token: expiration: ${REFRESH_TOKEN_EXPIRATION} #604800000 + +naver: + service: + url: ${NAVER_SERVICE_URL} + secretKey: ${NAVER_SERVICE_SECRET_KEY} + + +cloud: + aws: + s3: + bucket: ${BUCKET_NAME} + stack: + auto: false + region: + static: ap-northeast-2 + credentials: + instance-profile: true + access-key: ${S3_ACCESS_KEY} + secret-key: ${S3_SECRET_KEY}