엘리스트랙

최종 프로젝트 개발5

Zmann 2024. 5. 5. 22:40
728x90

 

 

 

 

 

유저, 모임, 챌린지에서 이미지 업로드를 해주어야 돼서 이미지 업로드 기능을 구현하였다.

https://innovation123.tistory.com/197

 

[Spring / S3] SpringBoot 프로젝트 - S3 이미지 업로드

이전 글에서 S3 bucket과 IAM을 생성하고 SpringBoot project에서 S3 접근에 사용할 accessKey와 secretKey를 얻는 것까지 다뤘다. 2024.01.21 - [DevOps] - [AWS/S3] Spring boot project 이미지 업로드를 위해 S3 버켓 만들기 [

innovation123.tistory.com

이 블로그를 참고했는데 큰 도움이 되었다.

 

 

 

 

@Slf4j
@RequiredArgsConstructor
@Component
public class S3ImageService {

    private final AmazonS3 amazonS3;

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

    public String upload(MultipartFile image) {
        if(image.isEmpty() || Objects.isNull(image.getOriginalFilename())){
            throw new ServiceLogicException(ExceptionCode.EMPTY_FILE);
        }
        return this.uploadImage(image);
    }

    private String uploadImage(MultipartFile image) {
        this.validateImageFileExtention(image.getOriginalFilename());
        try {
            return this.uploadImageToS3(image);
        } catch (IOException e) {
            throw new ServiceLogicException(ExceptionCode.IO_EXCEPTION_ON_IMAGE_UPLOAD);
        }
    }

    private void validateImageFileExtention(String filename) {
        int lastDotIndex = filename.lastIndexOf(".");
        if (lastDotIndex == -1) {
            throw new ServiceLogicException(ExceptionCode.NO_FILE_EXTENTION);
        }

        String extention = filename.substring(lastDotIndex + 1).toLowerCase();
        List<String> allowedExtentionList = Arrays.asList("jpg", "jpeg", "png", "gif");

        if (!allowedExtentionList.contains(extention)) {
            throw new ServiceLogicException(ExceptionCode.INVALID_FILE_EXTENTION);
        }
    }

    private String uploadImageToS3(MultipartFile image) throws IOException {
        String originalFilename = image.getOriginalFilename(); //원본 파일 명
        String extention = originalFilename.substring(originalFilename.lastIndexOf(".")); //확장자 명

        String s3FileName = UUID.randomUUID().toString().substring(0, 10) + originalFilename; //변경된 파일 명

        InputStream is = image.getInputStream();
        byte[] bytes = IOUtils.toByteArray(is);

        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType("image/" + extention);
        metadata.setContentLength(bytes.length);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);

        try{
            PutObjectRequest putObjectRequest =
                    new PutObjectRequest(bucketName, s3FileName, byteArrayInputStream, metadata);
                            //.withCannedAcl(CannedAccessControlList.PublicRead);
            amazonS3.putObject(putObjectRequest); // put image to S3
        }catch (Exception e){
            log.error("S3 이미지 업로드 중 에러 발생", e);
            throw new ServiceLogicException(ExceptionCode.PUT_OBJECT_EXCEPTION);
        }finally {
            byteArrayInputStream.close();
            is.close();
        }

        return amazonS3.getUrl(bucketName, s3FileName).toString();
    }

    public void deleteImageFromS3(String imageAddress){
        String key = getKeyFromImageAddress(imageAddress);
        try{
            amazonS3.deleteObject(new DeleteObjectRequest(bucketName, key));
        }catch (Exception e){
            throw new ServiceLogicException(ExceptionCode.IO_EXCEPTION_ON_IMAGE_DELETE);
        }
    }

    private String getKeyFromImageAddress(String imageAddress){
        try{
            URL url = new URL(imageAddress);
            String decodingKey = URLDecoder.decode(url.getPath(), "UTF-8");
            return decodingKey.substring(1); // 맨 앞의 '/' 제거
        }catch (MalformedURLException | UnsupportedEncodingException e){
            throw new ServiceLogicException(ExceptionCode.IO_EXCEPTION_ON_IMAGE_DELETE);
        }
    }
}

 

 

이미지를 업로드하는 서비스를 구현하고 기존 컨트롤러와 서비스에 적용을 해주었다.

 

@Operation(summary = "모임 생성 기능", description = "모임 생성")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "성공", content = @Content(schema = @Schema(implementation = MeetingResponseDto.class)))
    })
    @PostMapping
    public ResponseEntity<MeetingResponseDto> createMeeting(@AuthenticationPrincipal String email, @ModelAttribute @Valid MeetingDto meetingDto){
        meetingDto.setUserEmail(email);
        return ResponseEntity.ok(meetingService.createMeeting(meetingDto));
    }

 

@RequestBody로 프론트에서 받아오던 부분을 @ModelAttribute로 받아왔다. 프론트는 JSON에서 form-data로 넘겨주는 차이가 있다.

 

MultipartFile imageFile = meetingDto.getImageFile();
        String imageUrl = Constants.DEFAULT_URL + "meeting_default_image.png";
        if (imageFile != null && !imageFile.isEmpty()) {
            imageUrl = s3ImageService.upload(imageFile);
        }

 

그리고 서비스에서 이미지 업로드를 하는 기능을 넣어주었다. S3에 이미지를 넣어주면 해당 이미지의 url을 반환해 주어 그걸 저장해 주었다.

 

 

db에 이미지 url이 잘 저장되었다.

 

 

 

728x90