SportsEcho(2)

문정현·2024년 1월 8일
0

오늘은 Product, Hotdeal 두 도메인의 기본적인 CRUD를 마치고
하고 싶었던 S3 파일 업로드를 구현했다

AWS 콘솔에 들어가서
상단 검색창에 S3로 들어간다

버킷 만들어주고

  1. ACL 활성화됨
  2. 버킷 소유자 선호
  3. 모든 퍼블릭 액세스 차단 체크 해제 하고
    버킷 만들기 클릭

보안 자격 증명에서

엑세스 키를 만드는데
!꼭 CSV 파일을 다운받아서 키를 유실하지 않도록 한다!

1. YML

이번 프로젝트에서는 파일 크기를 10mb로 하기로 했는데 아직 팀원들과 논의는 못해봤다 내일 회의시간에 컨펌받고

spring.servlet.multipart.max-file-size: 10MB
spring.servlet.multipart.max-request-size: 10MB

cloud:
  aws:
    credentials:
      accessKey: ${aws_accessKey}
      secretKey: ${aws_secretKey}
    region:
      static: ap-northeast-2
    stack:
      auto: false
    s3:
      bucket: sports-echo

2. Config

@Configuration
public class AWSConfig {

    @Value("${cloud.aws.credentials.accessKey}")
    private String iamAccessKey; // IAM Access Key

    @Value("${cloud.aws.credentials.secretKey}")
    private String iamSecretKey; // IAM Secret Key

    private String region = "ap-northeast-2"; // Bucket Region (서울)

    @Bean
    public AmazonS3Client amazonS3Client() {
        BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(iamAccessKey, iamSecretKey);
        return (AmazonS3Client) AmazonS3ClientBuilder.standard()
            .withRegion(region)
            .withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
            .build();
    }

}

AWS configuration은 yml로 부터 key값을 가져오고 선언 값을 통해 S3 client를 빌드한다

3. Service

@Slf4j
@RequiredArgsConstructor
@Service
public class S3Uploader {

    private final AmazonS3Client amazonS3Client;

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

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

    public String upload(MultipartFile file, String filename) {
        File fileObj = converMultiPartFileToFile(file);
        amazonS3Client.putObject(new PutObjectRequest(bucket, filename, fileObj));
        fileObj.delete();

        String fileurl = amazonS3Client.getUrl(bucket, filename).toString();

        return fileurl;
    }

    private File converMultiPartFileToFile(MultipartFile file) {
        File convertedFile = new File(Objects.requireNonNull(file.getOriginalFilename()));
        try (FileOutputStream fileOutputStream = new FileOutputStream(convertedFile)) {
            fileOutputStream.write(file.getBytes());
        } catch (IOException e) {
            log.error("파일 변환 실패 : ", e);
        }
        return convertedFile;
    }

    public void deleteFile(String filename) {
        amazonS3Client.deleteObject(bucket, filename);
    }
}

@Service
@RequiredArgsConstructor
public class FileUploadService {

    private final S3Uploader s3Uploader;

    public String uploadFile(Member member, MultipartFile file, String identifier) {

//        if (member.getRole().equals(MemberRole.CUSTOMER)) {
//            throw new GlobalException(ProductErrorCode.NO_AUTHORIZATION);
//        }

        UUID uuid = UUID.randomUUID();
        String fileName = identifier + uuid;

        return s3Uploader.upload(file, fileName);
    }
}

아직 Member 회원가입에 일반 유저 USER_ROLE이 CUSTOMER밖에 구현이 안되어 있어서 인가 처리는 주석처리해뒀다 추후 파일업로드의 확장성을 위해 identifier로 어떤 도메인인지와 도메인 + uuid로 중복 파일 처리 로직이 들어가있다


성공적으로 구현 완료

삽질

@RequestBody로 MultipartFile file, String identifier를 받으려고 했으나

DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content-Type 'multipart/form-data;boundary=--------------------------203826436874826479100648;charset=UTF-8' is not supported]

PostMan JSON으로 잘 작동하는지 테스트 할려고 하니 에러 발생
multipart/form-data는 파일 업로드와 같이 많은 양의 데이터를 전송할 때 사용되는데, 이 경우 @RequestParam이나 @ModelAttribute를 사용해야 한다

@RequestBody UploadProductFileReqeuestDto uploadProductFileReqeuestDto // X

@RequestParam(value = "file")MultipartFile file,
@RequestParam(value = "identifier") String identifier // O
profile
주니어 개발자를 꿈꾸며

0개의 댓글