Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
Tags
- 격파르타비전공자
- 노베이스부트캠프
- java list 출력
- java참조자료형
- javaJRE
- 프로그래머스제일작은수
- 항해15기
- javaJVM
- java map 저장
- 코딩부트캠프후기
- java list 저장
- 항해99후기
- 격파르타후기
- java set 저장
- 프로그래머스
- java알고리즘
- java 자료구조 활용
- 격파르타합격후기
- 인터프린터언어
- 격파르타장점
- java최솟값구하기
- java set 출력
- java map 출력
- 작은수제거하기
- java map
- java알고리즘문제풀이
- java기본자료형
- sqld자격증합격
- 컴파일
- 비전공자sqld
Archives
- Today
- Total
코딩과 결혼합니다
230901 - Spring 의 BufferedImage로 S3 이미지 리사이징하기 본문
728x90
이미지 리사이징에 BufferedImage를 사용한 이유
기존에는 원본 이미지를 S3에 바로 업로드 하고, 프런트에서 해당 원본 이미지를 사용하였다. 그러나 디바이스에 보여지는 사진 대비 너무 큰 이미지를 호출하는건 비효율적이며, S3에도 굳이 큰 파일들을 모두 넣는것 보다는 리사이징 한 사진을 넣는게 공간을 덜 낭비할 것이라는 판단으로 이미지 리사이징을 하게 되었다.
이미지 리사이징을 하기 위해 BufferedImage 사용했다.
Java의 기본 라이브러리로 제공되므로 별도의 외부 라이브러리나 프레임워크 설치 없이 사용할 수 있고, resizeImage 메서드와 같이 크기를 변경하는 작업을 간단하게 구현할 수 있기 때문이다.
@Service
@RequiredArgsConstructor
@Transactional //(readOnly = true)
public class PostService {
private final AmazonS3Service amazonS3Service;
private final PostRepository postRepository;
private final PostImgRepository postImgRepository;
private final PostLikeRepository postLikeRepository;
private final PostScrapRepository postScrapRepository;
private final AlarmRepository alarmRepository;
private final ReportRepository reportRepository;
private final NotificationService notificationService;
@PersistenceContext
private EntityManager entityManager;
@Value("${cloud.aws.s3.bucket}")
private String bucketName;
//게시물 생성 : 게시물을 업로드하고 이미지를 저장
public MessageResponseDto upload(PostRequestDto requestDto, User user, List<MultipartFile> photos) throws IOException {
requestDto.validateCategory();
//본문에서 해시태그 추출
List<String> hashtags = extractHashtagsFromContent(requestDto);
//S3에 사진 업로드
List<PostImg> postImgList = amazonS3Service.uploadPhotosToS3AndCreatePostImages(photos);
Post post = new Post(requestDto, user);
postRepository.save(post);
associatePostImagesWithPost(post, postImgList);
// 게시물에 연결된 해시태그를 구독하는 사용자 찾기
for (String hashtag : hashtags) {
List<User> subscribers = findSubscribersForHashtag(hashtag);
for (User subscriber : subscribers) {
// 로그인한 사용자와 게시물을 올린 사용자가 다를 때만 알림 생성
if (!subscriber.getId().equals(user.getId())) {
AlarmEventType eventType = AlarmEventType.NEW_POST_WITH_HASHTAG; // 댓글에 대한 알림 타입 설정
Boolean isRead = false; // 초기값으로 미읽음 상태 설정
String notificationMessage = post.getContent(); // 알림 메시지 설정
LocalDateTime registeredAt = LocalDateTime.now(); // 알림 생성 시간 설정
String userImg = user.getProfileImageUrl();
String hashtagName = hashtag;
Alarm alarm = new Alarm(post, subscriber, eventType, isRead, notificationMessage, registeredAt, userImg, hashtagName);
alarmRepository.save(alarm);
notificationService.notifyAddEvent(subscriber, subscriber.isHashtagAlarm());
}
}
}
return new MessageResponseDto("파일 저장 성공");
}
원래는 postService 단에서 S3업로드를 하는 메서드 까지 모두 다뤘는데 가독성이 너무 떨어져서 S3업로드와 관련된 메서드는 AmazonS3Service로 분리 하였다.
Post를 업로드 할 때 사진 파일을 S3에 저장하게 되는데, 저장하기 전에 이미지를 너비 500으로 리사이징 하고 가로 세로 너비가 줄어든 만큼 높이도 비율에 맞춰 저장할 것이다.
AmazonS3Service - 리사이징
@Service
@RequiredArgsConstructor
public class AmazonS3Service {
private final AmazonS3Client amazonS3Client;
@Value("${cloud.aws.s3.bucket}")
private String bucketName;
//이미지를 S3에 업로드하고, PostImg 객체를 생성하여 리스트에 추가
public List<PostImg> uploadPhotosToS3AndCreatePostImages(List<MultipartFile> photos) throws IOException {
List<PostImg> postImgList = new ArrayList<>();
if (photos != null && !photos.isEmpty()) {
for (MultipartFile photo : photos) {
String fileName = uploadPhotoToS3AndGetFileName(photo);
PostImg postImg = new PostImg(fileNameToURL(fileName), null);
postImgList.add(postImg);
}
}
return postImgList;
}
하나의 메서드에 코드가 너무 많으면 가독성이 떨어지니 알아보기 쉽게 메서드를 분리해 주었다.
//S3에 이미지를 업로드하고, 업로드된 파일의 이름을 반환
private String uploadPhotoToS3AndGetFileName(MultipartFile photo) throws IOException {
long size = photo.getSize();
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType(photo.getContentType());
objectMetadata.setContentLength(size);
objectMetadata.setContentDisposition("inline");
String prefix = UUID.randomUUID().toString();
String fileName = prefix + "_" + photo.getOriginalFilename();
String bucketFilePath = "photos/" + fileName;
InputStream inputStream = photo.getInputStream();
BufferedImage originalImage = ImageIO.read(inputStream);
int targetWidth = 500; // 가로 길이를 500px로 설정
// 이미지가 500px보다 크면 리사이즈
if (originalImage.getWidth() > targetWidth) {
BufferedImage resizedImage = resizeImage(originalImage, targetWidth);
// 리사이즈된 이미지를 S3에 업로드
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(resizedImage, "jpg", outputStream);
byte[] resizedImageBytes = outputStream.toByteArray();
objectMetadata.setContentLength(resizedImageBytes.length);
amazonS3Client.putObject(
new PutObjectRequest(bucketName, bucketFilePath, new ByteArrayInputStream(resizedImageBytes), objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead)
);
} else {
// 이미지가 500px 이하이면 그대로 S3에 업로드
amazonS3Client.putObject(
new PutObjectRequest(bucketName, bucketFilePath, inputStream, objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead)
);
}
return fileName;
}
이 코드는 S3 (Amazon Simple Storage Service)에 이미지를 업로드하고, 업로드된 파일의 이름을 반환하는 메소드이다.
- uploadPhotoToS3AndGetFileName 메소드는 MultipartFile 객체인 photo를 매개변수로 받는다. 이 객체는 클라이언트에서 전송된 이미지 파일이다.
- photo.getSize()를 통해 업로드할 이미지의 크기를 확인
- ObjectMetadata 객체를 생성하여 업로드할 파일의 메타데이터(크기, 유형 등)를 설정
- UUID 값을 생성하여 파일 이름에 사용할 prefix로 지정
- prefix + "_" + photo.getOriginalFilename()을 통해 원본 파일 이름과 prefix를 조합하여 새로운 파일 이름 생성
- S3 버킷 내에서 저장될 경로(bucketFilePath)와 실제 S3 버킷에 저장될 경로(photos/파일이름)를 설정
- photo.getInputStream()을 호출하여 MultipartFile에서 InputStream을 얻는다.
- InputStream으로부터 BufferedImage 객체인 originalImage를 읽어온다.
- 이미지의 가로 길이가 500px보다 큰 경우, resizeImage() 메소드를 사용하여 이미지 크기를 조정하고 500px보다 작으면 그대로 저장
- resizeImage() 메소드는 원본 이미지와 타겟 가로 길이(targetWidth) 값을 받아 리사이즈된 BufferedImage 객체(resizedImage)를 반환
- 리사이즈된 이미지(resizedImage)는 ByteArrayOutputStream에 쓰여진 뒤, 바이트 배열(resizedImageBytes) 형태로 변환된다.
- objectMetadata.setContentLength()을 호출하여 리사이즈된 이미지의 크기도 설정
- AmazonS3Client의 putObject() 메소드를 사용하여 리사이즈된 이미지와 objectMetadata 정보가 포함된 PutObjectRequest 객체를 전달하고, CannedAccessControlList.PublicRead ACL(Access Control List) 옵션으로 S3 버킷에 업로드
public BufferedImage resizeImage(BufferedImage originalImage, int targetWidth) {
double aspectRatio = (double) originalImage.getHeight() / originalImage.getWidth();
int targetHeight= (int) (targetWidth * aspectRatio);
BufferedImage scaledImge= new BufferedImage(targetWidth,targetHeight ,BufferedImage.TYPE_INT_RGB);
Graphics2D g2d= scaledImge.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(originalImage ,0 ,0 ,targetWidth,targetHeight,null );
g2d.dispose();
return scaledImge;
}
private String fileNameToURL(String fileName) {
return "https://living-in-seoul.s3.ap-northeast-2.amazonaws.com/photos/" + fileName;
}
1.resizeImage(BufferedImage originalImage, int targetWidth) 메소드
이 메소드는 원본 이미지의 크기를 변경하여 새로운 이미지를 반환하는 역할을 한다.
- 먼저 원본 이미지의 가로 세로 비율(aspect ratio)을 계산
- 그 다음, 이 비율을 사용하여 변경할 이미지의 높이(targetHeight)를 계산
- 새로운 BufferedImage 객체(scaledImage)를 생성. 이 객체는 변경된 크기와 RGB 색상 모델을 가진다.
- Graphics2D 객체(g2d)를 생성하고, Bilinear Interpolation(양선형 보간법) 방식으로 렌더링 힌트 설정을 한다. 이렇게 하면 이미지 확대/축소 시 부드러운 결과물을 얻을 수 있다.
- drawImage 함수를 사용해 원본 이미지를 새롭게 계산된 너비와 높이에 맞게 그린다.
- g2d.dispose() 호출하여 그래픽 리소스들을 정리해주고, 리사이즈된 이미지(scaledImge)를 반환한다.
2.fileNameToURL(String fileName) 메소드
이 메소드는 파일 이름을 받아 해당 파일의 URL 주소 문자열로 변환
- 주어진 파일 이름에 대한 S3 버킷 내 위치 URL 문자열("https://living-in-seoul.s3.ap-northeast-2.amazonaws.com/photos/" + fileName) 을 만들어 반환
📌 결과
리사이징 전

리사이징 후

'코딩과 매일매일♥ > Seoulvival' 카테고리의 다른 글
| 230906 - @Transactional을 걸어도 더티체킹이 안돼! (0) | 2023.09.06 |
|---|---|
| 230902 - (코드리팩토링) api 6개를 3개로 합치기!! (0) | 2023.09.02 |
| 230831 - 알림기능 구현하기(isRead true로 바꾸기 / SSE 연결하기 ) (0) | 2023.08.31 |
| 230830 - 알림기능 구현하기(해시태그 구독하기/ 알림 구독하기) (0) | 2023.08.31 |
| 230829 - 알림기능 구현하기(알림 리스트) (0) | 2023.08.29 |