버킷 이름은 전역에서 고유해야하고, 공백 또는 대문자를 포함할 수 없다.
오른쪽 상단 계정 - 보안 자격 증명
- 사용자 = 하나의 접근 계정
- 애플리케이션에서 버킷에 접근을 위해 필요한 계정
- 추가된 사용자로부터 발급된
Access Key
와Secret Key
를 활용해 Spring Boot Application에서 버킷에 접근 가능
왼쪽 탭 사용자 - 사용자 추가
사용자명은 자유
기존 정책 직접 연결 - 정책 필터 : S3 검색 - AmazonS3FullAccess 선택
태그 추가는 추가 설정 없이 넘어감
작성 제대로 했는지 검토!
사용자 추가 성공적으로 완료
dependencies {
// AWS S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
}
application.yml
에 작성!
- bucket 이름은 S3에서 생성했던 bucket명과 동일하게 적어야하는 것 같다.
- 다르게 적었더니 no such bucket이라고 버킷을 찾지 못한다.
cloud:
aws:
s3:
bucket: carrotmarketclone # {버킷명}
credentials:
access-key: {발급 받은 Access key}
secret-key: {발급 받은 Secret key}
region:
static: ap-northeast-2
auto: false
stack:
auto: false
@Value
로application.yml
에 작성한 값 읽어온다.
amazonS3Client
객체 생성해서Bean
으로 주입
@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 AmazonS3 amazonS3Client() {
AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(region)
.build();
}
}
@Slf4j
@RequiredArgsConstructor
@Service
public class S3Uploader {
private final AmazonS3Client amazonS3Client;
@Value("${cloud.aws.s3.bucket}")
private String bucket;
// MultipartFile을 전달받아 File로 전환한 후 S3에 업로드
public String upload(MultipartFile multipartFile, String dirName) throws IOException { // dirName의 디렉토리가 S3 Bucket 내부에 생성됨
File uploadFile = convert(multipartFile)
.orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File 전환 실패"));
return upload(uploadFile, dirName);
}
private String upload(File uploadFile, String dirName) {
String fileName = dirName + "/" + uploadFile.getName();
String uploadImageUrl = putS3(uploadFile, fileName);
removeNewFile(uploadFile); // convert()함수로 인해서 로컬에 생성된 File 삭제 (MultipartFile -> File 전환 하며 로컬에 파일 생성됨)
return uploadImageUrl; // 업로드된 파일의 S3 URL 주소 반환
}
private String putS3(File uploadFile, String fileName) {
amazonS3Client.putObject(
new PutObjectRequest(bucket, fileName, uploadFile)
.withCannedAcl(CannedAccessControlList.PublicRead) // PublicRead 권한으로 업로드 됨
);
return amazonS3Client.getUrl(bucket, fileName).toString();
}
private void removeNewFile(File targetFile) {
if(targetFile.delete()) {
log.info("파일이 삭제되었습니다.");
}else {
log.info("파일이 삭제되지 못했습니다.");
}
}
private Optional<File> convert(MultipartFile file) throws IOException {
File convertFile = new File(file.getOriginalFilename()); // 업로드한 파일의 이름
if(convertFile.createNewFile()) {
try (FileOutputStream fos = new FileOutputStream(convertFile)) {
fos.write(file.getBytes());
}
return Optional.of(convertFile);
}
return Optional.empty();
}
}
@RestController
@RequestMapping("/boards")
@RequiredArgsConstructor
public class BoardController {
private final BoardService boardService;
private final FileService fileService;
private final S3Uploader s3Uploader;
/**
* 생성
*/
@PostMapping("")
public BaseResponse<PostBoardRes> create(
@RequestPart(value = "postBoardReq") PostBoardReq postBoardReq,
@RequestPart(value = "boardImg", required = false) MultipartFile multipartFile){
String fileName = "";
if(multipartFile != null){ // 파일 업로드한 경우에만
try{// 파일 업로드
fileName = s3Uploader.upload(multipartFile, "images"); // S3 버킷의 images 디렉토리 안에 저장됨
System.out.println("fileName = " + fileName);
}catch (IOException e){
return new BaseResponse<>(FAIL_FILE_CHANGE);
}
}
}
aws sdk 에러
build.gradle에 spring-cloud-starter-aws 의존성 주입시, 로컬 환경은 aws 환경이 아니라서 발생하는 에러
com.amazonaws.sdk.disableEc2Metadata
설정
@SpringBootApplication
class에 설정
@SpringBootApplication
public class CarrotMarketCloneApplication {
static {
System.setProperty("com.amazonaws.sdk.disableEc2Metadata", "true");
}
public static void main(String[] args) {
SpringApplication.run(CarrotMarketCloneApplication.class, args);
}
}
다만, 아래와 같은 Warning은 발생
Unable to retrieve the requested metadata. EC2 Instance Metadata Service is disabled
메세지 거슬릴 경우
application.yml
에 로깅 수준Error
로 변경하면 Warning 안뜬다고 한다!logging: level: com: amazonaws: util: EC2MetadataUtils: ERROR
application.yml에서 버킷명을 S3에서 생성한 버킷명과 동일하게 적어주었다.
버킷 선택
권한 탭
버킷 정책 - 편집
버킷 ARN 복사한 뒤, 정책 생성기 클릭
Actions
생성된 정책 복사
다시 버킷 정책 - 편집
복사해뒀던 정책 붙여넣기 - 변경 사항 저장
body : form-data 형식
body에 이미지 첨부하고 요청 보냈더니 요청 성공 메세지를 받았다!
images 폴더에 첨부했던 snowBear2.jpg가 들어있는 것을 확인!
[AWS] Spring Boot에서 AWS S3와 연계한 파일 업로드처리