Spring Boot 이미지 업로드
처음에 이미지 업로드는 서버에 임의로 지정해놓은 디렉터리에 저장되도록 구현했다.
private PostImage getPostImage(MultipartFile file, Post post) throws IOException {
if (file != null) {
String originalFilename = file.getOriginalFilename();
String saveFileName = createSaveFileName(originalFilename);
file.transferTo(new File(getFullPath(saveFileName))); // 여기서 임의의 위치로 이미지 저장
String filePath = uploadPath + saveFileName;
String contentType = file.getContentType();
PostImage image = PostImage.builder()
.fileName(originalFilename)
.saveFileName(saveFileName)
.contentType(contentType)
.filePath(filePath)
.post(post)
.build();
return image;
}
return PostImage.builder()
.fileName(null)
.saveFileName(null)
.contentType(null)
.filePath(null)
.post(post)
.build();
}
private String createSaveFileName(String originalFilename) {
String ext = extractExt(originalFilename);
String uuid = UUID.randomUUID().toString();
return uuid + "." + ext;
}
private String extractExt(String originalFilename) {
int pos = originalFilename.lastIndexOf(".");
return originalFilename.substring(pos + 1);
}
private String getFullPath(String filename) {
return uploadPath + filename;
}
서버에 이미지 데이터를 저장할 수도 있지만 S3를 이용해 이미지를 따로 저장하는 것이 더 효율적이므로 S3를 사용해보고 싶었다.
S3 설정
S3를 설정하려고 하니 거의 모든 블로그가 옛날에 사용하던 Dependency로 이제 더이상 업데이트도 되지 않고 취약점이 발견된 것 같다. 그래서 최신 버전으로 사용해보려고 방법을 찾아보다 dependency 주입부터 난관에 부딪혔다.
Dependency
implementation 'io.awspring.cloud:spring-cloud-aws-s3:3.1.0'
이것을 통해서 S3를 사용할 수 있게 된다.
cloud:
aws:
s3:
bucket:
credentials:
access-key:
secret-key:
region:
static: ap-northeast-2
auto: false
stack:
auto: false
S3에서 만든 버킷의 이름, 액세스 키 시크릿 키를 모두 기입해주면 된다.
버킷 생성 방법에 대해서는 따로 언급하지 않겠다.
S3Config
package com.nbcampif.ifstagram.global.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
@Configuration
public class S3Config {
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
@Value("${cloud.aws.region.static}")
private String region;
@Bean
public S3Client amazonS3() {
AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKey, secretKey);
return S3Client.builder()
.region(Region.of(region))
.credentialsProvider(StaticCredentialsProvider.create(credentials))
.build();
}
}
Config를 구성하는 것도 뭔가 많이 바껴서 찾아보고 하느라 시간이 오래 걸렸다.
이렇게 액세스 키와 시크릿 키를 통해 접근을 허용해주면 된다.
개발 구현
private PostImage getPostImage(MultipartFile file, Post post) {
try {
String originalFilename = file.getOriginalFilename();
String extension = StringUtils.getFilenameExtension(originalFilename);
String saveFileName = createSaveFileName(originalFilename);
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucket)
.key(uploadPath + saveFileName)
.contentType(extension)
.build();
PutObjectResponse response = s3Client
.putObject(putObjectRequest, RequestBody.fromBytes(file.getBytes()));
String filePath = uploadPath + saveFileName;
String contentType = file.getContentType();
if(response.sdkHttpResponse().statusText().orElse("FAIL").equals("OK")){
PostImage image = PostImage.builder()
.fileName(originalFilename)
.saveFileName(saveFileName)
.contentType(contentType)
.filePath(filePath)
.post(post)
.build();
return image;
}else{
throw new IllegalStateException("AWS에 파일을 올리는데 실패했습니다.");
}
} catch(IOException ie){
throw new RuntimeException(ie.getMessage());
} catch (S3Exception ae){
throw new RuntimeException(ae.getMessage());
} catch (IllegalStateException se){
throw new RuntimeException(se.getMessage());
}
}
PutObjectRequest를 사용하여 이미지를 S3로 업로드하고 S3에 업로드가 성공하면 데이터베이스에 이미지 정보를 저장하도록 하였다.
public String getImage(Long id) throws MalformedURLException {
PostImage image = postImageRepository.findById(id).orElse(null);
if (image == null || image.getFilePath() == null) {
return null;
}
GetUrlRequest request = GetUrlRequest
.builder()
.bucket(bucket)
.key(image.getFilePath())
.build();
return s3Client.utilities().getUrl(request).toExternalForm();
}
이미지를 가져오는 것을 이미지 주소를 넘겨주면 프론트에서 이미지 태크에 넣어주면서 이미지를 볼 수 있도록 하였다.
이전에는 GetUrl이라는 메서드가 있었는데 지금은 utilities메서드 안에 있다.
GetUrlRequest를 사용하여 이미지를 가져오고 전체 url를 보내주면 된다.
'기타 > 개발일기' 카테고리의 다른 글
[Spring Security] JWT 특성을 이용한 인증 처리 리팩토링 (0) | 2024.03.21 |
---|---|
Spring Boot에서 Redis 사용하기 (0) | 2024.03.15 |
[TIL] Jackson, Controller, DTO, JDBC (0) | 2024.01.18 |
[TIL] Gradle, Tomcat, 테스트 코드, Lombok, Spring MVC, 정적 및 동적 페이지 (0) | 2024.01.17 |
[TIL] 모던 자바(Java 8) (0) | 2024.01.05 |