미니프로젝트와 클론프로젝트에서 모두 유용하게 사용되는 S3 버킷을 이용한 사진 업로드 API 구현.
https://giron.tistory.com/55 S3생성 및 IAM계정 설정 참조
단!
이부분은 활성화 함으로 선택해주자 안그러면 에러를 만날 수 있다.
참조 사이트를 통해서 버킷생성과 IAM계정 설정까지만 마친 후
다음 절차를 따르자! 오늘을 기준으로 각종 오류를 만나면서 변경된점들을 반영한 코드이다....
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
입력해야할 부분을 잘 확인하고 작성하자.
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
datasource:
username: 사용 데이터베이스의 username 입력
url: 사용 데이터베이스의 url 입력
password: ''
h2:
console:
enabled: 'true'
thymeleaf:
cache: 'false'
jpa:
properties:
hibernate:
format_sql: 'true'
show_sql: 'true'
hibernate:
ddl-auto: update
jwt:
secret:
key: 토큰 키 값
cloud:
aws:
credentials:
access-key: 발급받은 access-key 반드시 '' 안에 입력 -> 'ssdasd'
secret-key: 발급받은 secret-key 반드시 '' 안에 입력 -> 'ssdasd'
region:
static: 'ap-northeast-2'
s3:
bucket: '버킷이름'
stack:
auto: false
logging:
level:
com:
amazonaws:
util:
EC2MetadataUtils: error
나는 서비스 항목에 그냥 추가했다.. 개인적으로 생각한 주석을 함께 추가.
@Slf4j
@RequiredArgsConstructor
@Component
public class S3Uploader {
private final AmazonS3Client amazonS3Client;
@Value("${cloud.aws.s3.bucket}")
private String bucket;
public String upload(MultipartFile multipartFile) throws IOException {
//MultipartFile을 전달 받고
//S3에 Multipartfile 타입은 전송이 안됩니다.
File uploadFile = convert(multipartFile)
.orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File로 전환이 실패했습니다."));
return upload(uploadFile);
}
private String upload(File uploadFile) {
String fileName = "static" + "/" + uploadFile.getName();
String uploadImageUrl = putS3(uploadFile, fileName);
removeNewFile(uploadFile);
return uploadImageUrl;
}
// 전환된 File을 S3에 public 읽기 권한으로 put
// 외부에서 정적 파일을 읽을 수 있도록 하기 위함입니다.
private String putS3(File uploadFile, String fileName) {
amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, uploadFile).withCannedAcl(CannedAccessControlList.PublicRead));
return amazonS3Client.getUrl(bucket, fileName).toString();
}
// 로컬에 생성된 File 삭제
// Multipartfile -> File로 전환되면서 로컬에 파일 생성된것을 삭제합니다.
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(Objects.requireNonNull(file.getOriginalFilename()));//파일이 있나없나 검사!
if(convertFile.createNewFile()) {//createNewFile로 새로운 파일을 만들어라
try (FileOutputStream fos = new FileOutputStream(convertFile)) { //convertFile를 OutputStream으로 만들어라
fos.write(file.getBytes());//fos 내부의 바이트 타입의 데이터를 가져와서 저장
}
return Optional.of(convertFile); //Null인지 아닌지 판별
}
return Optional.empty(); //빈 객체를 반환?
}
}
이부분은 구현하고자하는 방식에 따라 다르게 구현할 수 있으나 필자읙 경우 ModelAttribute를 선택했다.
@PostMapping("/pins/create")
public MessageResponseDto create(@ModelAttribute PinRequestDto requestDto,@AuthenticationPrincipal UserDetailsImpl userDetails)throws IOException{
return pinService.create(userDetails.getUser(),requestDto);
}