항해99/TIL | WIL

2023.02.25 (48일)

태감새 2023. 2. 26. 00:04

Day 48


1. S3

오늘은 S3를 이용해서 이미지를 업로드하고 DB에는 이미지URL을 저장하는 방법을 공부했다.

 

참고자료

파일을 저장하는 서비스. Simple Storage Service

사전 준비

  • S3 버킷 만들기
  • 사용자 추가해서 S3에 등록
  • accessKey랑 secretKey 받기

에러

  • 환경변수 추가
  • Edit Configuration -> build and run에 아래 문구 추가
  • Dcom.amazonaws.sdk.disableEc2Metadata=true

 

  • 스프링 부트 용량 제한
    스프링 부트는 용량제한이 1MB다. 그래서 1MB 이상의 파일이 들어오면 FileSizeLimitExceededException 발생
    그래서 application.properties에 추가설정 필요
# 파일 용량 제한 해제 
spring.servlet.multipart.max-file-size=128MB 
spring.servlet.multipart.max-request-size=128MB 
spring.servlet.multipart.enabled=true

 

spring

  • build.gradle

implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

  • application.properties
// access key
cloud.aws.credentials.accessKey=access key값  
// secret key
cloud.aws.credentials.secretKey=secret key값  
// 버킷 이름
cloud.aws.s3.bucket=버킷 이름
cloud.aws.region.static=ap-northeast-2  
cloud.aws.stack.auto-=false
  • S3Config
@Configuration  
public class S3Config {  
    @Value("${cloud.aws.credentials.accessKey}")  
    private String accessKey;  

    @Value("${cloud.aws.credentials.secretKey}")  
    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();  
    }  
}
  • TestController
@RestController  
@RequestMapping  
@RequiredArgsConstructor  
public class TestController {  

    private final TestService testService;  

    @PostMapping("/test")  
    public Long saveFile(@RequestParam(value = "image") MultipartFile image) {  
        Long testId = testService.keepDiary(image);  
        return testId;  
    }  
}
  • TestService
@Service  
@RequiredArgsConstructor  
public class TestService {  

    private final ImageRepository imageRepository;  
    private final S3Uploader s3Uploader;  

@Transactional  
public Long keepDiary(MultipartFile image) {  
    if (!image.isEmpty()) {  
        String storedFileName = s3Uploader.upload(image, "image");  
        System.out.println("storedFileName = " + storedFileName);  
        Image savedImage = imageRepository.save(new Image(storedFileName));  
        return savedImage.getId();  
    }  
    return null;  
}
}
  • S3Uploader
private final AmazonS3Client amazonS3Client;  

@Value("${cloud.aws.s3.bucket}")  
private String bucket;  

// MultipartFile을 전달받아 File로 전환한 후 S3에 업로드  
public String upload(MultipartFile multipartFile, String dirName) {  
    // multipartFile : 이미지, dirName : image  
    try {  
        // 1. mulipartFile을 File로 변환  
        File uploadFile = convert(multipartFile)  
                .orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File 전환 실패"));  
        return upload(uploadFile, dirName);  
    } catch (IOException e) {  
        throw new IllegalArgumentException("MultipartFile -> File 전환 실패");  
    }  
}  

private Optional<File> convert(MultipartFile file) throws  IOException {  
    // 1-1.multiFile의 이름을 가져와서 새로운 File객체에 입력  
    File convertFile = new File(file.getOriginalFilename());  
    // 1-2. convertFile.createNewFile() : convertFile과 같은 이름을 가진 파일이 없으면 생성 후 true반환, 있으면 false반환  
    if(convertFile.createNewFile()) {  
        // 1-3. FileOutputStram : byte단위로 파일을 기록하는 클래스 / 파일 생성됨  
        try (FileOutputStream fos = new FileOutputStream(convertFile)) {  
            // 1-4.file의 내용을 바이트로 변환해서 반환  
            fos.write(file.getBytes());  
        }  
        return Optional.of(convertFile);  
    }  
    return Optional.empty();  
}  

// 2. uploadFile : File로 변환된 multipartFile, dirName : "image"    private String upload(File uploadFile, String dirName) {  
    // 2-1. fileName(S3 URI)  
    String fileName = dirName + "/" + uploadFile.getName();  
    log.info("key : " + fileName);  
    // 3. uploadImageUrl에 image_url저장, S3에 파일 저장  
    String uploadImageUrl = putS3(uploadFile, fileName);  
    log.info("image_url : " + uploadImageUrl);  

    // 4. 로컬에 생성된 File 삭제 (MultipartFile -> File 전환 하며 로컬에 파일 생성됨)  
    removeNewFile(uploadFile);  

    // 5. 업로드된 파일의 S3 URL 주소 반환  
    return uploadImageUrl;  
}  

// 3-1. uploadFile : File로 변환된 multipartFile, FileName : key    private String putS3(File uploadFile, String fileName) {  
    amazonS3Client.putObject(  
            new PutObjectRequest(bucket, fileName, uploadFile)  
                    .withCannedAcl(CannedAccessControlList.PublicRead) // PublicRead 권한으로 업로드 됨  
    );  
    // 3-2. S3에 저장 후 URL을 가져와서 반환  
    return amazonS3Client.getUrl(bucket, fileName).toString();  
}  

// 4.  
private void removeNewFile(File targetFile) {  
    if(targetFile.delete()) {  
        log.info("파일이 삭제되었습니다.");  
    }else {  
        log.info("파일이 삭제되지 못했습니다.");  
    }  
}  
}
/**
== S3Uploader == 
 MultipartFile을 전달받아 File로 전환한 후 S3에 업로드

mulipartFile을 File로 변환
1-1. multiFile의 이름을 가져와서 새로운 File객체에 입력
1-2. convertFile.createNewFile() : convertFile과 같은 이름을 가진 파일이 없으면 생성 후 true반환, 있으면 false반환
1-3. FileOutputStram : byte단위로 파일을 기록하는 클래스 / 파일 생성됨
uploadFile : File로 변환된 multipartFile, dirName : "image"
2-1. fileName(S3 URI) * 3. uploadImageUrl(객체URL)에 image_url저장, S3에 파일 저장
3-1. uploadFile : File로 변환된 multipartFile, FileName : key
3-2. S3에 저장 후 URL을 가져와서 반환
로컬에 생성된 File 삭제 (MultipartFile -> File 전환 하며 로컬에 파일 생성됨)
업로드된 파일의 S3 URL 주소 반환
/

 

'항해99 > TIL | WIL' 카테고리의 다른 글

2023.02.27 (49일)  (0) 2023.02.27
WIL (6주)  (0) 2023.02.26
2023.02.24 (47일)  (0) 2023.02.25
2023.02.23 (46일)  (0) 2023.02.23
2023.02.22 (45일)  (0) 2023.02.23