DOing
[AWS] S3를 Spring에서 사용하기 - 이미지 업로드 본문
[ 버킷 정책 설정 ]
버킷 정책 수정한다.
퍼블릭 엑세스 차단 해제를 하고, 버킷 정책을 설정하는 방법을 사용할 것이다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::{버킷명}/*"
}
]
}
정책이 잘 적용되었는지 확인하려면, 파일을 업로드 해보면된다.
[ pom.xml ]
aws관련 의존성을 추가한다.
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.1001</version>
</dependency>
aws-java-sdk이 아닌 aws-java-sdk-s3를 받아야한다. 기능이 동작하지 않는 것은 아니지만 aws-java-sdk로 하면 모든 SDK를 가져오게 되어 너무 다운을 많이 받는다. 이때문에 패키징 시간이 크게 늘어났었다.
[ 파일 업로드 ]
기존의 코드에서는 로컬 컴퓨터 내에 이미지를 저장을 했다. (C:\\phoneMall\\upload)
그러나 이제는 AWS로 배포를 할 계획이였기때문에 로컬컴퓨터가 아닌 S3에 저장을 하는 코드로 변경이 필요하다.
기존의 코드를 최대한 건들지 않는 선에서 작업하였다.
<기존 코드 - RestController>
private void imageFolderSave(MultipartFile mainImage, List<ProductImageVO> imagelist, String imageType) {
String uploadFolder = "c:\\phoneMall\\upload";
// make folder
String uploadFolderPath = getFolder();
File uploadPath = new File(uploadFolder, uploadFolderPath);
if(!uploadPath.exists()) {
uploadPath.mkdirs();
}
UUID uuid = UUID.randomUUID();
String uploadImageName = uuid.toString()+"_"+mainImage.getOriginalFilename();
try {
// original image save
File saveImage = new File(uploadPath, uploadImageName);
mainImage.transferTo(saveImage);
if(imageType.equals("mainImage")) {
// thumbnail image create, save
FileOutputStream thumbnail = new FileOutputStream(new File(uploadPath, "s_"+uploadImageName));
Thumbnailator.createThumbnail(mainImage.getInputStream(), thumbnail, 400, 333);
thumbnail.close();
}
// productImageVO create
imagelist.add(new ProductImageVO(uuid.toString(), uploadFolderPath.toString().replace("\\", "/"), mainImage.getOriginalFilename(), imageType, null));
}catch(Exception e){log.error(e.getMessage());}
}
<수정 코드 - RestController>
private void imageFolderSave(MultipartFile image, List<ProductImageVO> imagelist, String imageType) {
// make folder
String uploadFolderPath = getFolder();
UUID uuid = UUID.randomUUID();
String uploadImageName = uuid.toString()+"_"+image.getOriginalFilename();
try {
String s3Path = uploadFolderPath+"/"+uploadImageName;
s3service.uploadFile(image, s3Path);
if(imageType.equals("mainImage")) {
String thumbs3Path = uploadFolderPath+"/s_"+uploadImageName;
s3service.uploadThumbFile(image, thumbs3Path);
}
// productImageVO create for DB
imagelist.add(new ProductImageVO(uuid.toString(), uploadFolderPath.toString().replace("\\", "/"), image.getOriginalFilename(), imageType, null));
}catch(Exception e){log.error(e.getMessage());}
}
주요 흐름을 보자면 다음과 같다.
1. 원본이미지를 S3에 저장
-> 저장시, 이미지 파일 이름 중복 예방
1) UUID를 랜덤생성
2) 년/월/일 폴더를 만들어 저장
2. 메인이미지라면 섬네일을 만들어서 S3에 저장
3. DB에 이미지 정보를 저장(uuid나 파일 위치, 이름, 이미지 타입 등등,,)
@Log4j
@Service
@RequiredArgsConstructor
@Getter
public class S3Service {
final private AmazonS3 s3Client;
String bucketName = "버킷명";
String accessKey = "액세스 키";
String secretKey = "액세스 시크릿 키";
public S3Service() {
AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
this.s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(Regions.AP_NORTHEAST_2)
.build();
}
// upload original image file
public void uploadFile(MultipartFile image, String s3Path) {
try {
// set ObjectMatadata
byte[] bytes = IOUtils.toByteArray(image.getInputStream());
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(bytes.length);
objectMetadata.setContentType(image.getContentType());
// save in S3
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
this.s3Client.putObject(this.bucketName, s3Path.replace(File.separatorChar, '/'), byteArrayInputStream, objectMetadata);
byteArrayInputStream.close();
}catch(Exception e){log.error(e.getMessage());}
}
// upload thumbnail image file
public void uploadThumbFile(MultipartFile image, String thumbs3Path) {
try {
// make thumbnail image for s3
BufferedImage bufferImage = ImageIO.read(image.getInputStream());
BufferedImage thumbnailImage = Thumbnails.of(bufferImage).size(400, 333).asBufferedImage();
ByteArrayOutputStream thumbOutput = new ByteArrayOutputStream();
String imageType = image.getContentType();
ImageIO.write(thumbnailImage, imageType.substring(imageType.indexOf("/")+1), thumbOutput);
// set metadata
ObjectMetadata thumbObjectMetadata = new ObjectMetadata();
byte[] thumbBytes = thumbOutput.toByteArray();
thumbObjectMetadata.setContentLength(thumbBytes.length);
thumbObjectMetadata.setContentType(image.getContentType());
// save in s3
InputStream thumbInput = new ByteArrayInputStream(thumbBytes);
this.s3Client.putObject(this.bucketName, thumbs3Path.replace(File.separatorChar, '/'), thumbInput, thumbObjectMetadata);
thumbInput.close();
thumbOutput.close();
}catch(Exception e){log.error(e.getMessage());}
}
}
- BasicAWSCredentials("액세스키", "액세스 시크릿키");
: 이때 액세스키가 깃허브에 노출되지않도록 유의해야 한다.
: accessKey와 secretKey를 이용하여 자격증명 객체를 얻는다.
- AmazonS3ClientBuilder.standard().withCredentials().build();
: 얻은 자격증명 객체를 이용해서 AmazonS3ClientBuilder로 S3 Client를 가져온다.
(AmazonS3Client가 deprecated됨에 따라, AmazonS3ClientBuilder를 사용했다.)
: 한번 빈으로 등록하고 다음에는 의존성 주입을 통해 가져다가 쓰면된다. 그러면 이제부터 내가 만든 S3에 접속할 수 있다.
- objectMetadata.setContentLength(bytes.length);
: metadata로 contentLength를 설정하지 않으면 S3에 업로드는 되나, 다음과 같은 에러로그가 나온다.
: [io-8443-exec-65] c.amazonaws.services.s3.AmazonS3Client : No content length specified for stream data.
- objectMetadata.setContentType(image.getContentType());
: metadata contentType을 설정하지않으면, S3의 contentType에 jpg, png이런식으로만 올라가게된다.
: jpg, png으로만 되면 url으로 접속시에 무조건 다운로드가 되게 된다. 하지만 contentType에 image/jpg로 명시하게되면 바로 다운로드가 아닌 이미지를 브라우저로 조회만도 가능하다.
- BufferedImage thumbnailImage = Thumbnails.of(bufferImage).size(400, 333).asBufferedImage();
: 사실 AWS S3를 사용하게되면 주로 람다를 이용해서 섬네일 이미지를 만든다. 하지만 그렇게 되면 기존 코드와 차이가 많이 생기다보니 Thumbnailator를 계속 사용하기로 하였다.
: 기존 코드에서처럼 Thumbnailator.createThumbnail(bufferImage, 400, 333);를 사용하려했지만, 이렇게 되면 BufferedImage를 한번 더 거치게되서인지 섬네일 이미지 색상에 문제가 생겼다. 그래서 thumbnailator.Thumbnails를 사용하기로 했다.
참고 출처 :
docs.aws.amazon.com/ko_kr/sdk-for-java/v1/developer-guide/examples-s3-objects.html
www.joinc.co.kr/w/Site/C/Documents/CprogramingForLinuxEnv/Ch4_VarAndOper
윈도우 C:\\와 리눅스/home/차이
'AWS' 카테고리의 다른 글
[AWS] S3 Spring에서 사용하기 - 이미지 삭제 (0) | 2021.04.23 |
---|---|
[AWS] S3를 Spring에서 사용하기 - 이미지 조회 (0) | 2021.04.23 |
[AWS] S3 버킷 생성, 객체 업로드하기 (0) | 2021.04.21 |
[AWS] Spring 프로젝트 배포 (0) | 2021.04.20 |
[AWS] Spring 프로젝트 배포 준비하기 (0) | 2021.04.20 |