AWS S3
의 버킷 생성 및 IAM
사용자 추가 등은 쉽게 검색을 통해 찾아볼 수 있으니, 필요시 찾아보시길 바랍니다.
//aws S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
YML
파일에 설정 정보를 분리해서 관리할 수 있는 방법을 새롭게 찾아 이 방식을 사용해보았다.
application-*.yml
이름으로 생성한 파일들을 application.yml
파일의 Spring.profiles.include
속성안에 *
값들을 넣어 어플리케이션 구동시 읽을 수 있도록 할 수 있다.
# s3 연습
spring:
profiles:
include:
- aws
- credentials
- redis
# AWS 설정 정보
cloud:
aws:
s3:
bucket: s3-image-pratice
folder:
folderName1: user/
folderName2: post/
region:
static: ap-northeast-2
stack:
auto: false
기능마다
S3 버킷
의 다른 폴더에 이미지들을 저장하여 관리하기 위해서folder
속성을 추가로 사용하였다.
# S3 key 관리
cloud:
aws:
credentials:
accessKey: [엑세스 키]
secretKey: [시크릿 키]
application.yml
기입한 AWS S3
의 정보들을 사용하여 Component를 만들어 놓았다.
@Component
@Getter
public class S3Component {
@Value("${cloud.aws.s3.bucket}")
private String bucket;
@Value("${cloud.aws.s3.folder.folderName1}")
private String userFolder;
@Value("${cloud.aws.s3.folder.folderName2}")
private String postFolder;
}
AWS S3
를 이용한 방식을 말고도 다른 클라우드 서비스를 이용하여 파일 업로드 기능을 구현해야하는 상황도 있을 것이다.
이러한 확장성을 고려하여 파일 업로드와 관련된 기능을 인터페이스와 구현체로 분리시켜 놓았다.
public interface FileService {
//파일 업로드
String uploadFile(MultipartFile file, FileFolder fileFolder);
//파일 삭제
void deleteFile(String fileName);
//파일 URL 조회
String getFileUrl(String fileName);
//파일 다운로드
byte[] downloadFile(String fileName) throws FileNotFoundException;
//폴더 조회
String getFileFolder(FileFolder fileFolder);
}
@Component
@RequiredArgsConstructor
public class S3Service implements FileService{
private final S3Component s3Component;
private final AmazonS3 amazonS3;
@Override
public String uploadFile(MultipartFile file, FileFolder fileFolder) {
//파일 이름 생성
String fileName = getFileFolder(fileFolder) + createFileName(file.getOriginalFilename());
//파일 변환
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(file.getSize());
objectMetadata.setContentType(file.getContentType());
//파일 업로드
try(InputStream inputStream = file.getInputStream()) {
amazonS3.putObject(
new PutObjectRequest(s3Component.getBucket(), fileName, inputStream, objectMetadata).withCannedAcl(CannedAccessControlList.PublicReadWrite)
);
} catch (IOException e) {
throw new IllegalArgumentException(String.format("파일 변환 중 에러가 발생하였습니다. (%s)", file.getOriginalFilename()));
}
return fileName;
}
//파일 이름 생성 로직
private String createFileName(String originalFileName) {
return UUID.randomUUID().toString().concat(getFileExtension(originalFileName));
}
//파일의 확장자명을 가져오는 로직
private String getFileExtension(String fileName){
try{
return fileName.substring(fileName.lastIndexOf("."));
}catch(StringIndexOutOfBoundsException e) {
throw new IllegalArgumentException(String.format("잘못된 형식의 파일 (%s) 입니다.",fileName));
}
}
}
FileFolder
을 Enum으로 구성하여 버킷내 폴더를 기능에 따라 선택할 수 있게 하였다.
AWS S3
가 제공해주는 putObject
메소드를 살펴보게 되면, 2가지 방식이 존재한다.
AWS S3
에 이미지를 업로드 하기 위해, 전달받은 이미지(바이너리 파일)을 서버내에MultipartFile
의 InputStream(파일의 Byte)와 ObjectMetaData(파일의 정보)를 사용해서 파일을 업로드 하는 방식.임시 파일을 만드는 것은 불필요한 과정이라고 생각이 들어 2번 방식을 사용하였다.
@Component
@RequiredArgsConstructor
public class S3Service implements FileService{
private final S3Component s3Component;
private final AmazonS3 amazonS3;
@Override
public void deleteFile(String fileName) {
amazonS3.deleteObject(new DeleteObjectRequest(s3Component.getBucket(), fileName));
}
}
@Component
@RequiredArgsConstructor
public class S3Service implements FileService{
private final S3Component s3Component;
private final AmazonS3 amazonS3;
@Override
public String getFileUrl(String fileName) {
return amazonS3.getUrl(s3Component.getBucket(), fileName).toString();
}
}
AWS S3
에 저장된 파일 이름에 해당하는 URL(경로)를 알아내는 함수이다.
@Component
@RequiredArgsConstructor
public class S3Service implements FileService{
private final S3Component s3Component;
private final AmazonS3 amazonS3;
@Override
public byte[] downloadFile(String fileName) throws FileNotFoundException {
//파일 유무 확인
validateFileExists(fileName);
S3Object s3Object = amazonS3.getObject(s3Component.getBucket(), fileName);
S3ObjectInputStream s3ObjectContent = s3Object.getObjectContent();
try {
return IOUtils.toByteArray(s3ObjectContent);
}catch (IOException e ){
throw new FileDownloadFailedException();
}
}
private void validateFileExists(String fileName) throws FileNotFoundException {
if(!amazonS3.doesObjectExist(s3Component.getBucket(), fileName))
throw new FileNotFoundException();
}
}
다운로드 받을 파일이 S3
에 존재하는지 검증을 먼저 하도록 하였다.
다운로드 받을 파일이 있다면, amazonaws Utils
함수를 사용해서 파일(이미지)의 InputStream을 읽어 byte배열로 바꾸어 주었다.
@Component
@RequiredArgsConstructor
public class S3Service implements FileService{
private final S3Component s3Component;
private final AmazonS3 amazonS3;
@Override
public String getFileFolder(FileFolder fileFolder) {
String folder = "";
if(fileFolder == FileFolder.USER_IMAGES) {
folder = s3Component.getUserFolder();
}else if(fileFolder ==FileFolder.POST_IMAGES){
folder = s3Component.getPostFolder();
}
return folder;
}
}
기능에 따라 다른 폴더에 파일(이미지)를 저장하기 위해서, 저장될 폴더를 선택하는 함수이다.
이번 포스터에서는
AWS S3
를 이용한 파일(이미지) 처리에 기능 구현에 집중하였다.해당 기능을 테스트 하기 위해서 간단히
Article
,Image
엔티티와 서비스, 레포지토리를 구현하여 컨트롤러를 통해 테스트 해보았다.해당 포스터에는 없지만 아래 github 링크를 통해 완전한 코드를 볼 수 있다.
https://github.com/BonSik-Koo/Funtion-pratice/tree/master/src/main/java/project/AMS/awsS3
잘 보고 갑니다.. 도움 많이 될 것 같아요