Spring boot AWS S3를 이용한 File Upload/Download
안녕하십니까
Java Spring boot를 이용한 API 개발 중 AWS S3에 이미지 파일을 관리해야 하는 작업이 있어 진행을 해봤습니다.
Java에서 S3나 그 외에 관련된 기능들을 사용하려면 SDK를 Dependency추가해줘야 합니다.
Gradle 4.6 이상
group 'aws.test'
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
implementation 'com.amazonaws:aws-java-sdk-bom:1.11.228'
implementation 'com.amazonaws:aws-java-sdk-s3'
}
Gradle 4.6 미만
group 'aws.test'
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "io.spring.gradle:dependency-management-plugin:1.0.3.RELEASE"
}
}
apply plugin: "io.spring.dependency-management"
dependencyManagement {
imports {
mavenBom 'com.amazonaws:aws-java-sdk-bom:1.11.228'
}
}
dependencies {
compile 'com.amazonaws:aws-java-sdk-s3'
}
저 같은 경우는 IDE에서는 3 버전을, 실 서버에서는 5 버전 대를 사용했기 때문에 dependencies에도 넣어주고 mavenBom도 추가해줬습니다.
추가를 완료 하였다면 이제 사용을 하면 되는데, 사용을 하기 전 우리는 서비스 클라이언트를 생성해줘야 합니다.
이 서비스 클라이언트를 만들어 줘야 S3, CloudWatch 등 여러 서비스 들을 이용하실 수 있습니다.
Amazon Web Services에 요청하려면 먼저 서비스 클라이언트 객체를 만듭니다. 권장 방법은 서비스 클라이언트 빌더를 사용하는 것입니다.
각 AWS 서비스에는 서비스 API의 각 작업에 대한 메서드를 포함하는 서비스 인터페이스가 있습니다.
예를 들어 Amazon DynamoDB의 서비스 인터페이스 이름은 AmazonDynamoDB입니다. 각 서비스 인터페이스마다 서비스 인터페이스의 구현을 생성하는 데 사용할 수 있는 해당하는 클라이언트 빌더가 있습니다. DynamoDB용 클라이언트 빌더 클래스 이름은 AmazonDynamoDBClientBuilder입니다.
우리는 S3를 이용할 것이니 서비스 인터페이스 이름은 AmazonS3라는 인터페이스를 사용할 것이며, 클라이언트 빌더 이름은 AmazonS3 ClientBuilder가 될 것입니다.
@Configuration
@EnableWebMvc
public class AWSConfiguration implements WebMvcConfigurer {
@Value("${AWS.ACESSKEY}")
private String accessKey;
@Value("${AWS.SECRETKEY}")
private String secretKey;
@Bean
public BasicAWSCredentials AwsCredentials() {
BasicAWSCredentials AwsCreds = new BasicAWSCredentials(accessKey, secretKey);
return AwsCreds;
}
@Bean
public AmazonS3 AwsS3Client() {
AmazonS3 s3Builder = AmazonS3ClientBuilder.standard()
.withRegion(Regions.AP_NORTHEAST_2)
.withCredentials(new AWSStaticCredentialsProvider(this.AwsCredentials()))
.build();
return s3Builder;
}
이제 기능을 구현하면 되는데요. 저는 S3를 이용한 GET, UPLOAD, DELETE를 구현할 것입니다.
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class AwsS3Service {
@Autowired
private AmazonS3 s3Client;
@Value("${AWS.BUCKET}")
private String bucketName;
public void uploadObject(MultipartFile multipartFile, String storedFileName) throws IOException {
SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd");
ObjectMetadata omd = new ObjectMetadata();
omd.setContentType(multipartFile.getContentType());
omd.setContentLength(multipartFile.getSize());
omd.setHeader("filename", multipartFile.getOriginalFilename());
// Copy file to the target location (Replacing existing file with the same name)
s3Client.putObject(new PutObjectRequest(bucketName + "/" + date.format(new Date()), storedFileName,
multipartFile.getInputStream(), omd));
}
public void deleteObject(String date, String storedFileName) throws AmazonServiceException {
s3Client.deleteObject(new DeleteObjectRequest(bucketName + "/" + date, storedFileName));
}
public Resource getObject(String date, String storedFileName) throws IOException {
S3Object o = s3Client.getObject(new GetObjectRequest(bucketName + "/" + date, storedFileName));
S3ObjectInputStream objectInputStream = o.getObjectContent();
byte[] bytes = IOUtils.toByteArray(objectInputStream);
Resource resource = new ByteArrayResource(bytes);
return resource;
}
}
위의 코드에서 보면 bucketName + "/" + date가 있는데, 이 부분이 폴더 이름이 될 것입니다.
그리고 getObject 부분은 예제와 좀 다르게 구현을 하였는데, Restful API 방식으로 데이터를 전달하기 위해 org.springframework.core.io.Resource를 활용하였습니다.
그리고 그냥 resposne로 보낼 시 File name이 null로 전송되기 때문에 다음과 같이 설정을 해줘야 합니다.
Resource resource = imageService.loadFileAsResource(date, fileName);
// Try to determine file's content type
String contentType = null;
try {
contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
} catch (IOException ex) {
log.info("Could not determine file type.");
}
// Fallback to the default content type if type could not be determined
if(contentType == null) {
contentType = "application/octet-stream";
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.body(resource);
postman으로 테스트 할 시 제대로 파일을 받을 수 있는 것을 확인 하실 수 있습니다.
출처 및 참고
Java SDK 사용
https://docs.aws.amazon.com/ko_kr/sdk-for-java/v1/developer-guide/setup-project-gradle.html
Java S3