From 9f3dc14c0ef250577185c033c767ac8b65606ede Mon Sep 17 00:00:00 2001 From: MJJ Date: Fri, 5 Jul 2024 07:13:50 +0900 Subject: [PATCH 1/3] feat: s3 --- .gitignore | 4 +- build.gradle | 1 + .../hackaton/snapspot/S3/S3Controller.java | 39 ++++++++++++ .../umc/hackaton/snapspot/S3/S3Service.java | 63 +++++++++++++++++++ .../hackaton/snapspot/config/S3Config.java | 36 +++++++++++ .../spot/controller/SpotController.java | 5 +- .../snapspot/spot/dto/SpotRequestDto.java | 1 - .../hackaton/snapspot/spot/entity/Spot.java | 2 +- .../snapspot/spot/service/SpotService.java | 13 +++- src/main/resources/application.yml | 3 + 10 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/umc/hackaton/snapspot/S3/S3Controller.java create mode 100644 src/main/java/com/umc/hackaton/snapspot/S3/S3Service.java create mode 100644 src/main/java/com/umc/hackaton/snapspot/config/S3Config.java diff --git a/.gitignore b/.gitignore index f61f7ab..78ebee6 100644 --- a/.gitignore +++ b/.gitignore @@ -144,4 +144,6 @@ gradle-app.setting # End of https://www.toptal.com/developers/gitignore/api/java,intellij+all,gradle -.env \ No newline at end of file +.env + +application-secret.yml \ No newline at end of file diff --git a/build.gradle b/build.gradle index 5f6a3fb..c82d248 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,7 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' implementation 'org.springframework.security:spring-security-crypto' implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' // AWS compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' runtimeOnly 'com.h2database:h2' diff --git a/src/main/java/com/umc/hackaton/snapspot/S3/S3Controller.java b/src/main/java/com/umc/hackaton/snapspot/S3/S3Controller.java new file mode 100644 index 0000000..f667029 --- /dev/null +++ b/src/main/java/com/umc/hackaton/snapspot/S3/S3Controller.java @@ -0,0 +1,39 @@ +package com.umc.hackaton.snapspot.S3; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequestMapping("/api/v1/s3") +@RequiredArgsConstructor +@Slf4j +public class S3Controller { + + private final S3Service s3Service; + + @PostMapping("/upload") + public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) { + try { + String fileUrl = s3Service.saveFile(file); + return ResponseEntity.ok(fileUrl); + } catch (Exception e) { + log.error(e.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("S3 업로드에 실패했습니다."); + } + } + + @GetMapping("/file") + public ResponseEntity getFile(@RequestParam("fileName") String fileName) { + try { + String fileUrl = s3Service.getFile(fileName); + return ResponseEntity.ok(fileUrl); + } catch (Exception e) { + log.error(e.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("S3 파일 조회에 실패했습니다."); + } + } +} diff --git a/src/main/java/com/umc/hackaton/snapspot/S3/S3Service.java b/src/main/java/com/umc/hackaton/snapspot/S3/S3Service.java new file mode 100644 index 0000000..a7939f5 --- /dev/null +++ b/src/main/java/com/umc/hackaton/snapspot/S3/S3Service.java @@ -0,0 +1,63 @@ +package com.umc.hackaton.snapspot.S3; + + +import com.amazonaws.SdkClientException; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.AmazonS3Exception; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@Service +@Slf4j +@RequiredArgsConstructor +public class S3Service { + + private final AmazonS3 amazonS3Client; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + @Transactional + public String saveFile(MultipartFile multipartFile) { + + String originalFilename = multipartFile.getOriginalFilename(); + try { + + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(multipartFile.getSize()); + metadata.setContentType(multipartFile.getContentType()); + + amazonS3Client.putObject(new PutObjectRequest(bucket, originalFilename, multipartFile.getInputStream(), metadata)); + return amazonS3Client.getUrl(bucket, originalFilename).toString(); + } catch (IOException e) { + // 파일 입출력 예외 처리 + e.printStackTrace(); + return "File upload failed due to an IO error: " + e.getMessage(); + } catch (AmazonS3Exception e) { + // AWS S3 관련 예외 처리 + e.printStackTrace(); + return "File upload failed due to an Amazon S3 error: " + e.getMessage(); + } catch (SdkClientException e) { + // AWS SDK 관련 클라이언트 예외 처리 + e.printStackTrace(); + return "File upload failed due to an SDK client error: " + e.getMessage(); + } catch (Exception e) { + // 기타 모든 예외 처리 + e.printStackTrace(); + return "File upload failed due to an unexpected error: " + e.getMessage(); + } + } + @Transactional + public String getFile(String fileName) { + return amazonS3Client.getUrl(bucket, fileName).toString(); + + } +} diff --git a/src/main/java/com/umc/hackaton/snapspot/config/S3Config.java b/src/main/java/com/umc/hackaton/snapspot/config/S3Config.java new file mode 100644 index 0000000..aedd2dc --- /dev/null +++ b/src/main/java/com/umc/hackaton/snapspot/config/S3Config.java @@ -0,0 +1,36 @@ +package com.umc.hackaton.snapspot.config; + + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Slf4j +@Configuration +public class S3Config { + + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3 amazonS3() { + BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey); + + return AmazonS3ClientBuilder.standard() + .withRegion(region) + .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) + .build(); + } +} + diff --git a/src/main/java/com/umc/hackaton/snapspot/spot/controller/SpotController.java b/src/main/java/com/umc/hackaton/snapspot/spot/controller/SpotController.java index 5638934..2b4c4d8 100644 --- a/src/main/java/com/umc/hackaton/snapspot/spot/controller/SpotController.java +++ b/src/main/java/com/umc/hackaton/snapspot/spot/controller/SpotController.java @@ -9,6 +9,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -23,9 +24,9 @@ public class SpotController { private final SpotService spotService; @PostMapping - public ResponseEntity upload(@RequestBody SpotRequestDto dto) { + public ResponseEntity upload(@RequestBody SpotRequestDto dto, MultipartFile file) { try { - Spot spot = spotService.upload(dto); + Spot spot = spotService.upload(dto, file); return ResponseEntity.ok().body(spot); } catch (Exception e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("스팟 업로드에 실패하였습니다."); diff --git a/src/main/java/com/umc/hackaton/snapspot/spot/dto/SpotRequestDto.java b/src/main/java/com/umc/hackaton/snapspot/spot/dto/SpotRequestDto.java index 399973c..b955a66 100644 --- a/src/main/java/com/umc/hackaton/snapspot/spot/dto/SpotRequestDto.java +++ b/src/main/java/com/umc/hackaton/snapspot/spot/dto/SpotRequestDto.java @@ -13,7 +13,6 @@ public class SpotRequestDto { private Double longitude; private String title; private String description; - private String imgUrl; private List categoryNums; } diff --git a/src/main/java/com/umc/hackaton/snapspot/spot/entity/Spot.java b/src/main/java/com/umc/hackaton/snapspot/spot/entity/Spot.java index 3cd75ef..02ab274 100644 --- a/src/main/java/com/umc/hackaton/snapspot/spot/entity/Spot.java +++ b/src/main/java/com/umc/hackaton/snapspot/spot/entity/Spot.java @@ -59,7 +59,7 @@ public void decreaseLike() { public Spot update(SpotRequestDto dto){ this.description = dto.getDescription(); - this.imgUrl = dto.getImgUrl();; +// this.imgUrl = dto.getImgUrl();; this.title = dto.getTitle(); return this; } diff --git a/src/main/java/com/umc/hackaton/snapspot/spot/service/SpotService.java b/src/main/java/com/umc/hackaton/snapspot/spot/service/SpotService.java index 71d3705..e178506 100644 --- a/src/main/java/com/umc/hackaton/snapspot/spot/service/SpotService.java +++ b/src/main/java/com/umc/hackaton/snapspot/spot/service/SpotService.java @@ -1,5 +1,6 @@ package com.umc.hackaton.snapspot.spot.service; +import com.umc.hackaton.snapspot.S3.S3Service; import com.umc.hackaton.snapspot.category.entity.Category; import com.umc.hackaton.snapspot.category.entity.CategorySpot; import com.umc.hackaton.snapspot.category.repository.CategoryRepository; @@ -14,7 +15,10 @@ import com.umc.hackaton.snapspot.user.repository.UserRepository; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import java.util.ArrayList; import java.util.List; @@ -23,13 +27,18 @@ @Service @RequiredArgsConstructor public class SpotService { + private static final Logger log = LoggerFactory.getLogger(SpotService.class); private final SpotRepository spotRepository; private final UserRepository userRepository; private final CategoryRepository categoryRepository; private final CategorySpotRepository categorySpotRepository; + private final S3Service s3Service; @Transactional - public Spot upload(SpotRequestDto dto) { + public Spot upload(SpotRequestDto dto, MultipartFile file) { + + String imgUrl = s3Service.saveFile(file); + log.info("imgUrl: " + imgUrl); User user = userRepository.findUserById(dto.getUserId()); @@ -39,7 +48,7 @@ public Spot upload(SpotRequestDto dto) { spotDto.setLatitude(dto.getLatitude()); spotDto.setTitle(dto.getTitle()); spotDto.setDescription(dto.getDescription()); - spotDto.setImgUrl(dto.getImgUrl()); + spotDto.setImgUrl(imgUrl); Spot savedSpot = spotRepository.save(spotDto.toEntity()); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 84660ba..d19a5dc 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,4 +1,7 @@ spring: + profiles: + active: + - secret datasource: # driver-class-name: com.mysql.cj.jdbc.Driver # url: jdbc:mysql://localhost:3306/spot?useSSL=false&allowPublicKeyRetrieval=true From 544d9424c645d8759ecaf377c2ae75f0dad31da0 Mon Sep 17 00:00:00 2001 From: MJJ Date: Fri, 5 Jul 2024 07:24:31 +0900 Subject: [PATCH 2/3] feat: application-secret.yml ignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 78ebee6..703c853 100644 --- a/.gitignore +++ b/.gitignore @@ -146,4 +146,4 @@ gradle-app.setting .env -application-secret.yml \ No newline at end of file +src/main/resources/application-secret.yml \ No newline at end of file From 3732acd57503ac1718918bf5b6bef3a70f2716aa Mon Sep 17 00:00:00 2001 From: mashin2002 Date: Fri, 5 Jul 2024 08:51:36 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=EC=82=AC=EC=A7=84=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사진 등록 #46 --- src/main/java/com/umc/hackaton/snapspot/S3/S3Service.java | 1 + .../umc/hackaton/snapspot/spot/controller/SpotController.java | 2 +- .../com/umc/hackaton/snapspot/spot/service/SpotService.java | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/umc/hackaton/snapspot/S3/S3Service.java b/src/main/java/com/umc/hackaton/snapspot/S3/S3Service.java index a7939f5..4659dac 100644 --- a/src/main/java/com/umc/hackaton/snapspot/S3/S3Service.java +++ b/src/main/java/com/umc/hackaton/snapspot/S3/S3Service.java @@ -59,5 +59,6 @@ public String saveFile(MultipartFile multipartFile) { public String getFile(String fileName) { return amazonS3Client.getUrl(bucket, fileName).toString(); + } } diff --git a/src/main/java/com/umc/hackaton/snapspot/spot/controller/SpotController.java b/src/main/java/com/umc/hackaton/snapspot/spot/controller/SpotController.java index 2b4c4d8..252b59d 100644 --- a/src/main/java/com/umc/hackaton/snapspot/spot/controller/SpotController.java +++ b/src/main/java/com/umc/hackaton/snapspot/spot/controller/SpotController.java @@ -24,7 +24,7 @@ public class SpotController { private final SpotService spotService; @PostMapping - public ResponseEntity upload(@RequestBody SpotRequestDto dto, MultipartFile file) { + public ResponseEntity upload(@RequestPart(value = "file") MultipartFile file, @RequestPart SpotRequestDto dto) { try { Spot spot = spotService.upload(dto, file); return ResponseEntity.ok().body(spot); diff --git a/src/main/java/com/umc/hackaton/snapspot/spot/service/SpotService.java b/src/main/java/com/umc/hackaton/snapspot/spot/service/SpotService.java index e178506..55515a6 100644 --- a/src/main/java/com/umc/hackaton/snapspot/spot/service/SpotService.java +++ b/src/main/java/com/umc/hackaton/snapspot/spot/service/SpotService.java @@ -69,6 +69,8 @@ public Spot upload(SpotRequestDto dto, MultipartFile file) { return savedSpot; } + + @Transactional public Spot getSpotById(Long spotId) { // spotId를 사용하여 Repository를 통해 Spot을 가져옴