-
Amazon S3를 이용해서 파일 저장, 삭제하기Spring 2021. 11. 5. 22:54
이미 프로젝트의 로컬폴더에 이미지 저장하는 코드를 구현했었다. (FIle Uplaod Utils)
이것을 amazon S3를 통해서 원격으로 S3에 파일을 처리하도록 해보자.
먼저 aws에 가입해서 버킷을 만들어 주고 '내 보안자격 증명'에서 access_key와 secret_key을 만들어준다. (생략)
여기서 발급받은 access_key, secret_key는 절대! 외부로 노출되지 않도록 해야한다.
(해당 키를 public github에 올린다거나 하면 aws에서 연락이 왔다는 글도 봤다.)
결제 카드를 등록해서 가입하고 결제와 관련된 부분이므로 조심 또 조심해야한다.
(로그인 2차 보안도 해놓자)
S3 Dependency
<dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk</artifactId> <version>1.11.1000</version> </dependency>
https://docs.aws.amazon.com/ko_kr/sdk-for-java/v1/developer-guide/setup-project-maven.html
공식 사이트에서 해당 버전을 보고 추가해주었다.
application.properties에 값 설정
cloud.aws.s3.bucket=버킷 이름 cloud.aws.region=지역 설정 이름 (한국은 ap-northeast-2) cloud.aws.credentials.accessKey=발급받은 키 값 cloud.aws.credentials.secretKey=발급받은 시크릿 값
AmazonS3Config 생성
@Configuration public class AmazonConfig { @Value("${cloud.aws.s3.bucket}") private String bucket; @Value("${cloud.aws.region}") private String region; @Value("${cloud.aws.credentials.accessKey}") private String accessKey; @Value("${cloud.aws.credentials.secretKey}") private String secretKey; @Bean public AmazonS3 s3(){ AWSCredentials awsCredentials = new BasicAWSCredentials( accessKey, secretKey ); return AmazonS3ClientBuilder .standard() .withRegion(region) .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) .build(); } }
등록된 정보를 가져와서 AmazonsS3Client를 빌드해주는 config를 생성해주고 @Configuration으로 등록해준다.
AmazonS3Utils 생성
@Service @RequiredArgsConstructor public class AmazonS3Utils { @Value("${cloud.aws.s3.bucket}") private String bucket; @Value("${cloud.aws.region}") private String region; @Value("${cloud.aws.credentials.accessKey}") private String accessKey; @Value("${cloud.aws.credentials.secretKey}") private String secretKey; private final AmazonS3 s3; //버킷에 등록된 모든 폴더 불러오기 public List<String> listFolder(String folder){ ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request().withBucketName(bucket).withPrefix(folder); ListObjectsV2Result listObjectsV2Result = s3.listObjectsV2(listObjectsV2Request); ListIterator<S3ObjectSummary> listIterator = listObjectsV2Result.getObjectSummaries().listIterator(); List<String> list = new ArrayList<>(); while (listIterator.hasNext()){ S3ObjectSummary object = listIterator.next(); list.add(object.getKey()); } return list; } //파일 업로드 public String uploadFile(String folderName, MultipartFile multipartFile) throws IOException { File uploadFile = convert(multipartFile) .orElseThrow(()->new IllegalArgumentException("MultipartFile 형식을 File로 전환하는 데에 실패하였습니다.")); return upload(uploadFile, folderName); } //파일 삭제 public void deleteFile(String fileName){ DeleteObjectRequest request = new DeleteObjectRequest(bucket, fileName); s3.deleteObject(request); } //폴더 삭제 (폴더안의 모든 파일 삭제) public void removeFolder(String folderName){ ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request().withBucketName(bucket).withPrefix(folderName+"/"); ListObjectsV2Result listObjectsV2Result = s3.listObjectsV2(listObjectsV2Request); ListIterator<S3ObjectSummary> listIterator = listObjectsV2Result.getObjectSummaries().listIterator(); while (listIterator.hasNext()){ S3ObjectSummary objectSummary = listIterator.next(); DeleteObjectRequest request = new DeleteObjectRequest(bucket,objectSummary.getKey()); s3.deleteObject(request); System.out.println("Deleted " + objectSummary.getKey()); } } private String upload(File uploadFile, String folderName){ String fileName = folderName + "/" + uploadFile.getName(); String uploadImageUrl = putS3(uploadFile, fileName); removeNewFile(uploadFile); return uploadImageUrl; } //S3에 업로드 private String putS3(File uploadFile, String fileName){ s3.putObject(new PutObjectRequest(bucket, fileName, uploadFile).withCannedAcl(CannedAccessControlList.PublicRead)); return s3.getUrl(bucket, fileName).toString(); } //임시로 생성된 new file을 삭제해준다 private void removeNewFile(File targetFile){ if(targetFile.delete()){ System.out.println("File deleted."); }else{ System.out.println("File doesn't deleted."); } } //multipartFile을 File타입으로 변환해준다. (변환된 파일을 가지고 put해줄 것이다) private Optional<File> convert(MultipartFile file) throws IOException { File convertFile = new File(System.currentTimeMillis() + StringUtils.cleanPath(file.getOriginalFilename())); if(convertFile.createNewFile()){ try(FileOutputStream fos = new FileOutputStream(convertFile)){ fos.write(file.getBytes()); } return Optional.of(convertFile); } return Optional.empty(); } }
생성된 amazonS3Client를 가지고 정보를 처리한다. 여기서 필요한 기능을 만들어주자. (생성한 기능은 파일 업로드, 파일 삭제, 폴더 삭제, 폴더 리스트 가져오기)
여기서 파일을 생성할 때, 이름이 겹치지 않게 하기 위해서 System.currentTimeMillis()를 추가하여 파일 이름을 생성해주었다.
AmazonS3Util 사용하기
private final AmazonS3Utils amazonS3Utils; @Value("${AWS_S3_BUCKET_URL}") private String AWS_S3_BUCKET_URL; private void deleteProductImage(List<String> deletedImages){ for (String deletedImage : deletedImages) { productImageRepository.deleteByFilePath(deletedImage); String fileName = deletedImage.substring(AWS_S3_BUCKET_URL.length()+1); amazonS3Utils.deleteFile(fileName); } } private void saveProductImage(Product product, List<MultipartFile> multipartFiles){ multipartFiles.forEach(multipartFile -> { ProductImage productImage = new ProductImage(); try{ String uploadDir = "productUploads/" + product.getId(); String imageUrl = amazonS3Utils.uploadFile(uploadDir, multipartFile); String fileName = imageUrl.substring(AWS_S3_BUCKET_URL.length()+uploadDir.length()+2); productImage.setName(fileName); productImage.setFilePath(imageUrl); productImage.setProduct(product); productImageRepository.save(productImage); }catch (IOException ex){ new IOException("Could not save file : " + multipartFile.getOriginalFilename()); } }); }
위와 같이 AmazonS3Util을 이용해서 파일 생성과 삭제를 실행해준다.
(참고한 사이트)
https://shj7242.github.io/2017/12/28/Spring34/
https://songc92.tistory.com/51
https://gunbin91.github.io/aws/2020/01/14/aws_6_javas3.html
https://galid1.tistory.com/590
https://jojoldu.tistory.com/300
https://mighty96.github.io/til/s3-upload/
'Spring' 카테고리의 다른 글
multipart.MaxUploadSizeExceededException 오류 발생시 (0) 2021.11.06 Data too long for column 오류 발생시 (0) 2021.11.06 Springboot(Maven), React, MySQL 프로젝트 Heroku 배포하기 (0) 2021.11.05 SMTP 서버를 통한 이메일 보내기 (0) 2021.10.26 OAuth2 사용해서 react와 함께 소셜로그인 기능 만들기 (4) 2021.10.26