강잇
강이의 개발블로그
강잇
전체 방문자
오늘
어제
  • 분류 전체보기 (102)
    • Langauge (32)
      • Java-basic (29)
      • Java (3)
    • SpringBoot (7)
    • Algorithm (5)
      • BAEKJOON (5)
    • WEB (7)
      • HTML & CSS (7)
    • DB (1)
      • MySQL (1)
    • OS (17)
      • Mac (2)
      • Linux (4)
      • Terminal Command (11)
    • Computer Science (7)
      • Hard ware (1)
      • Database (1)
      • Data structure (2)
      • Algorithm (2)
      • Network (1)
    • Git (5)
      • 개념 (1)
      • 활용 (1)
      • Trouble Shooting (2)
    • ETC. (13)
      • Install (6)
      • IntelliJ (1)
      • Eclipse (2)
      • Error (3)
      • Tip (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 자바
  • CSS 속성
  • 알고리즘 공부
  • 메소드
  • CSS 박스 크기 설정
  • 메서드
  • 백준
  • til

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
강잇

강이의 개발블로그

[SpringBoot] AWS S3 파일(이미지) 업로드 및 삭제하기 구현
SpringBoot

[SpringBoot] AWS S3 파일(이미지) 업로드 및 삭제하기 구현

2022. 11. 28. 00:32

개발 환경

  • Build : Gradle
  • SpringBoot :  2.7.5
  • Java : 11
  • OS : Mac

요구사항

client로부터 form-data의 이미지 파일을 요청받으면 해당 이미지를 S3 버킷에 업로드한 뒤 DB에 버킷의 경로를 저장하려고 함.

이미지는 1개의 이미지를 요청 및 업로드함.

구현 - AWS 관련(버킷 생성 등) 설정은 프로젝트가 끝난 뒤 정리할 예정

의존성 추가하기

https://github.com/awspring/spring-cloud-aws

// AWS S3 Upload
implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.2'

S3 관련 환경변수 설정하기(yml)

cloud:
  aws:
    credentials:
      accessKey: # Access Key
      secretKey: # Secret Key
    s3:
      bucket: # 버킷 이름
    region:
      static: s3 버킷의 Region
    stack:
      auto: false
      
spring:
  servlet:
    multipart:
      max-file-size: # 파일 크기 ex) 20MB

AWS - EC2에서 Spring Cloud 프로젝트를 실행시키면 기본적으로 CloudFormation 구성을 시작한다.

설정한 CloudFormation이 없을 경우 프로젝트 실행이 안되니 사용하지 않도록 false로 설정한다.

cloud 관련 설정은 절대 공개가 되면 안되기 때문에 시스템 환경 변수 처리 또는 gitignore 처리를 하도록 한다!

AWS S3 Config

@Configuration
public class S3Config {
    @Value("${cloud.aws.credentials.accessKey}")
    private String accessKey;

    @Value("${cloud.aws.credentials.secretKey}")
    private String secretKey;

    @Value("${cloud.aws.region.static}")
    private String region;

    @Bean
    public AmazonS3 amazonS3Client() {
        AWSCredentials credentials =
                new BasicAWSCredentials(accessKey, secretKey);

        return AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withRegion(region)
                .build();
    }

}

 

AWS S3 Service

@RequiredArgsConstructor
@Service
public class AwsS3Service {

    private final AmazonS3Client amazonS3Client;

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

    public String uploadFile(MultipartFile multipartFile) {

        String fileName = createFileName(multipartFile.getOriginalFilename());
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(multipartFile.getSize());
        metadata.setContentType(multipartFile.getContentType());

        try (InputStream inputStream = multipartFile.getInputStream()) {
            amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, inputStream, metadata));
        } catch (IOException e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "이미지 업로드에 실패했습니다.");
        }
        return amazonS3Client.getUrl(bucket, fileName).toString();
    }

    // 이미지 수정으로 인해 기존 이미지 삭제 메소드
    public void deleteImage(String fileUrl) {
        String splitStr = ".com/";
        String fileName = fileUrl.substring(fileUrl.lastIndexOf(splitStr) + splitStr.length());

        amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, fileName));
    }

    private String createFileName(String fileName) {
        return UUID.randomUUID().toString().concat(getFileExtension(fileName));
    }

    private String getFileExtension(String fileName) {
        try {
            return fileName.substring(fileName.lastIndexOf("."));
        } catch (StringIndexOutOfBoundsException se) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "잘못된 형식의 파일(" + fileName + ") 입니다.");
        }
    }
}

uploadFile(MultipartFile multipartFile)

DB에 저장될 형식에 따라 리턴 값을 변경하면 되는데, 내 기준으로는 DB에 이미지 전체 URL을 저장하기로 해서 일단 위와 같이 코드를 작성했다. 위와 같이 저장할 경우 리턴 값은 https://"bucket-name"."region".amazonaws.com/"파일 이름.확장자" 형식으로 저장됨.

"파일 이름.확장자"만 필요할 경우 return fileName; 으로 수정하면 된다.

deleteImage(String fileUrl)

위에서도 말했듯이 DB에 저장되는 이미지 경로가 URL( https://"bucket-name"."region".amazonaws.com/"파일 이름.확장자")이기 때문에 ".com/"기준으로 자르는 과정을 따로 넣었다.

S3 파일을 삭제할 때 넘기는 값은 S3에 저장되어 있는 파일명.확장자 로 전달을 하면 된다.

createFileName(String fileName)

저장될 이름이 중복되지 않도록 UUID 클래스를 이용하여 파일명 앞 부분을 고유 값으로 넣어준다.

ArtworkService

S3에 업로드가 필요한 Service 단에서 위에서 작성한 S3Service를 DI 받은 후 각 로직에 적용시킨다.

@Service
@RequiredArgsConstructor
@Transactional
public class ArtworkService {

    ...
    
    private final AwsS3Service awsS3Service;

    // S3에 이미지 등록(저장)
    public void createArtwork(long memberId, long galleryId, ArtworkRequestDto requestDto) {
        
        ...
        
        // 요청받은 multipartFile(이미지)를 DI받은 awsS3Service - uploadFile()에 넘긴다.
        String imageRoot = awsS3Service.uploadFile("MultipartFile");
        
        ...
        
        // 저장된 경로를 DB에 저장하는 것이므로 무시해도 됩니다.
        artwork.setImagePath(imageRoot);

        artworkRepository.save(artwork);
    }
    
    // S3에 이미지 저장 및 기존 이미지 삭제
    public ArtworkResponseDto updateArtwork(long memberId, long galleryId, long artworkId, ArtworkRequestDto request) {
        
        ...
        // 받은 요청에 image가 존재할 경우
        if (image.isPresent()) {
            // 기존 DB에 저장되어 있는 url을 넘겨 S3에서 삭제함.
            awsS3Service.deleteImage(findArtwork.getImagePath());
            // 신규 이미지를 S3에 저장함.
            String s3Path = awsS3Service.uploadFile(image.get());
            artwork.setImagePath(s3Path);
        }
        
        ...
    }
}

테스트

팀원분께 부탁해서 기존 이미지를 삭제하였을 때 정상적으로 삭제하는 것을 확인할 수 있었다.

프로젝트가 끝나면 내 aws 계정으로 테스트하며 파일 추가 및 삭제되는 것을 추가적으로 작성할 예정...

이미지를 등록했을 때 h2 에 저장되는 경로
해당 url을 접속했을 때
이미지를 수정하였을 때
수정된 url을 접속하였을 때

Reference

https://www.sunny-son.space/spring/Springboot%EB%A1%9C%20S3%20%ED%8C%8C%EC%9D%BC%20%EC%97%85%EB%A1%9C%EB%93%9C/

 

Springboot로 S3 파일 업로드하기

이번 포스팅은 스프링에서 AWS S3 파일 업로드하는 방법입니다. 주로 이미지 파일을 올릴 때 많이 사용되곤 합니다. 1. 의존성 추가하기 build.gradle awspring/spring-cloud-aws Spring-Cloud-AWS 의존성을 추가합

www.sunny-son.space

https://jane514.tistory.com/10

 

AWS S3 이미지 업로드 Spring으로 사용해보기

대부분의 이미지 서버를 구축할 때는 AWS S3를 보편적으로 사용합니다. 저희 또한, S3를 애플리케이션 이미지 업로드 및 다운로드 서버로 선택하게 되었습니다. 큰 이유가 있기 보다는.. 서버를 사

jane514.tistory.com

 

저작자표시 (새창열림)

'SpringBoot' 카테고리의 다른 글

[Trouble Shooting] Web server failed to start. Port 8080 was already in use  (0) 2022.12.02
[SpringBoot] MockMvc - multipart() POST외 다른 HTTPMethod 사용하기  (0) 2022.11.26
[TroubleShooting] SpringBoot Controller Test - MockMvc 302 Found, 403 Forbidden  (0) 2022.11.24
[SpringDataJPA] 쿼리메서드 참조 객체의 필드 사용  (0) 2022.11.17
[Trouble Shooting] MaxUploadSizeExceededException  (0) 2022.11.03
    'SpringBoot' 카테고리의 다른 글
    • [Trouble Shooting] Web server failed to start. Port 8080 was already in use
    • [SpringBoot] MockMvc - multipart() POST외 다른 HTTPMethod 사용하기
    • [TroubleShooting] SpringBoot Controller Test - MockMvc 302 Found, 403 Forbidden
    • [SpringDataJPA] 쿼리메서드 참조 객체의 필드 사용
    강잇
    강잇
    학습한 내용을 정리 및 기록하는 블로그

    티스토리툴바