S3 연동을 위해 build.gradle에 aws sdk를 추가한다.
dependencies {
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.715'
...
}
aws에서 엑세스 키를 발급받은 후에 application.yml에 저장한다.
aws:
s3:
access-key: {엑세스 키}
secret-access-key: {시크릿 엑세스 키}
bucket: {버킷 명}
package com.palindrome.studit.global.config.aws;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class S3Config {
@Value("${aws.s3.access-key}")
private String accessKey;
@Value("${aws.s3.secret-key}")
private String secretKey;
@Bean
AmazonS3 amazonS3Client() {
return AmazonS3ClientBuilder.standard()
.withCredentials(
new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey))
)
.withRegion(Regions.AP_NORTHEAST_2)
.build();
}
}
위와 같이 S3Config를 설정하여 외부에서 amazonS3Client 빈을 주입받을 수 있도록 하였다.
private final AmazonS3 amazonS3Client;
@Value("${aws.s3.bucket") private String bucketName;
public void updateImage(MultipartFile file) throws IOException {
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(file.getSize());
objectMetadata.setContentType(file.getContentType());
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, file.getName(), file.getInputStream(), objectMetadata);
amazonS3Client.putObject(putObjectRequest);
}
위와 같이 주입받은 amazonS3Client를 통하여 이미지를 생성할 수 있다.
@PostMapping("/image")
public ResponseEntity<Object> uploadImage(@RequestPart("file") MultipartFile file) throws IOException {
imageService.updateImage(file);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
위와 같이 전달받은 파일을 그대로 저장할 수 있다.
하지만 DB에서 해당 파일 URL을 알고 접근하는 것이 필요할 수 있다.
@entity
@Getter
@Builder
public class Study {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long studyId;
@Size(max = 255)
private String studyImage;
}
위와 같이 Study 엔티티에 studyImage 필드가 있다.
이미지 명은 중요하지 않고 중복되면 안되기에 unique 옵션을 추가하였다.
@entity
@Getter
@Builder
public class Study {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long studyId;
@Size(max = 255)
@Column(unique = true)
private String studyImage;
public static String generateRandomStudyImageName() {
return "study/images/" + UUID.randomUUID().toString();
}
public void updateStudyImage(String fileName) {
this.studyImage = fileName;
}
}
public class StudyService {
private final StudyRepository studyRepository;
private final AmazonS3 amazonS3Client;
@Value("${aws.s3.bucket") private String bucketName;
@Transactional
public void uploadStudyImage(Long studyId, MultipartFile file) throws IOException {
Study study = studyRepository.findById(studyId).orElseThrow();
study.updateStudyImage(Study.generateRandomStudyImageName());
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(file.getSize());
objectMetadata.setContentType(file.getContentType());
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, file.getInputStream(), objectMetadata);
amazonS3Client.putObject(putObjectRequest);
}
public String getStudyImageUrl(Long studyId) {
Study study = studyRepository.findById(studyId).orElseThrow();
return amazonS3Client.getUrl(bucketName, study.getStudyImage()).toString();
}
위와 같이 스터디 이미지 변경 로직을 작성하여 DB에 이미지 URL을 저장하거나 저장된 URL을 파싱할 수 있다.