project 에서 사진을 업로드 기능을 만들어야 해서 사진을 저장하는 여러방법 중 AWS S3에 업로드 하고 url만 DB에 저장하는 방식을 선택하였다.
※ 이 포스트는 AWS 계정이 있고 S3 설정까지 했다는 전제하에 작성하였습니다.
implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4'
※org.springframework.cloud:spring-cloud-starter-aws ~
는 업데이트가 2021년도 이후로 되지 않으니 쓰지 말자
# 파일 업로드 크기 설정
spring:
servlet:
multipart:
max-file-size: 20MB # 파일 하나당 크기
max-request-size: 20MB # 전송하려는 총 파일 크기
cloud:
aws:
credentials:
accessKey: {AWS IAM AccessKey}
secretKey: {AWS IAM SecretKey}
s3:
bucket: {버킷 이름}
region:
static: {S3 버킷 지역}
stack:
auto: false
git에 업로드 하기 전 반드시 .gitignore 에 application.yml 추가하세요.
accessKey와 SecretKey 가 git에 올라갈 경우(public repository) 여러분의 계정은 비트코인 채굴장소가 될 수 있습니다.
@RequiredArgsConstructor
@RequestMapping("/v1/posts")
@RestController
public class PostsController {
@PostMapping("/create")
public ResponseEntity create(@RequestPart(value = "request") @Valid PostsDto.PostDto request,
@RequestPart(value = "file") List<MultipartFile> file) {
...
return new ResponseEntity<>(mapper.postsToResponseDto(response), HttpStatus.CREATED);
}
@RequestPart
어노테이션을 사용하여 multipart/form-data 요청을 받습니다.
만약 dto도 사용해야 한다면 dto 또한 @RequestPart
를 사용하여 데이터를 받아야 합니다.
@Configuration
public class AWSConfig {
@Value("${cloud.aws.credentials.accessKey}")
private String accessKey;
@Value("${cloud.aws.credentials.secretKey}")
private String accessSecret;
@Value("${cloud.aws.region.static}")
private String region;
@Bean
public AmazonS3 s3Client() {
AWSCredentials credentials = new BasicAWSCredentials(accessKey, accessSecret);
return AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(region).build();
}
}
@RequiredArgsConstructor
@Service
@Slf4j
public class AwsS3Service {
private final AmazonS3 amazonS3Client;
@Value("${cloud.aws.s3.bucket}")
private String bucketName; //버킷 이름
public String uploadFile(MultipartFile multipartFile) throws IOException {
validateFileExists(multipartFile); //파일이 있는지 확인하는 메서드
String fileName = CommonUtils.buildFileName(multipartFile.getOriginalFilename()); //fileName
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType(multipartFile.getContentType());
try (InputStream inputStream = multipartFile.getInputStream()) {
amazonS3Client.putObject(new PutObjectRequest(bucketName, fileName, inputStream, objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (Exception e) {
log.error("Can not upload image, ", e);
throw new RuntimeException("cannot upload image);
}
String url = amazonS3Client.getUrl(bucketName, fileName).toString();
return url;
}
...
private void validateFileExists(MultipartFile multipartFile) {
if(multipartFile.isEmpty()) {
throw new RuntimeException("file is empty");
}
}
파일 이름 생성할 때 사용되는 클래스
public class CommonUtils {
private static final String FILE_EXTENSION_SEPARATOR = ".";
public static String buildFileName(String originalFileName) {
int fileExtensionIndex = originalFileName.lastIndexOf(FILE_EXTENSION_SEPARATOR); //파일 확장자 구분선
String fileExtension = originalFileName.substring(fileExtensionIndex); //파일 확장자
String fileName = originalFileName.substring(0, fileExtensionIndex); 파일 이름
String now = String.valueOf(System.currentTimeMillis()); 파일 업로드 시간
return fileName + "_" + now + fileExtension;
}
}
reference
https://www.sunny-son.space/spring/Springboot%EB%A1%9C%20S3%20%ED%8C%8C%EC%9D%BC%20%EC%97%85%EB%A1%9C%EB%93%9C/
https://github.com/Java-Techie-jt/s3-file-storage-example/tree/10bf1924b0bc7fbec6b8985849750cb2df89255c