본문 바로가기

BackEnd : Spring

[AWS] SpringBoot + AWS S3에 이미지 업로드하기

AWS S3 버킷 생성

내 pc로 접근할 것이기 때문에 모든 퍼블릭 액세스 차단 해제

나머지 설정은 건드리지 않음

버킷이 생성되었다

유저의 프로필 사진들을 저장할 profile_imgs 폴더를 만들어준다

IAM 만들기

사용자 생성 완료

IAM accessKey, secretKey 얻기

IAM - 사용자 - 보안 자격 증명

액세스 키 만들기

버킷 접근 권한 설정

코드를 다 알맞게 작성했는데 이미지가 안불러와져서 알아보니 버킷에 getobject가 가능하도록 권한을 설정해줘야 했다!

나의 버킷에서 권한 정책을 이렇게 수정하였다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::talenttreebucket/*"
        }
    ]
}

S3Config

package com.springboot.portfolio.config;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@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 amazonS3() {
        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

        return AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withRegion(region)
                .build();
    }
}

application.properties

spring.profiles.include=private

이렇게 지정해놓고 s3에 관한 설정들은 application-private.properties 에 저장해놓았다. 

private 설정들은 민감하므로 gitignore에 추가해놓았다.

application-private.properties

# S3
cloud.aws.credentials.accessKey={your-access-key}
cloud.aws.credentials.secretKey={your-secret-key}
cloud.aws.s3.bucketName={your-bucket-name}
cloud.aws.region.static={your-region}
cloud.aws.stack.auto-=false

LoginController

기존 사용자의 정보 수정 시 이미지를 업로드하는 controller에서

local 서버에 이미지를 저장하는 방식에서 s3에 이미지를 업로드 하는 방식으로 수정하였다.

@Controller
@RequiredArgsConstructor
public class LoginController {
    // private static String UPLOAD_DIR = System.getProperty("user.dir") + "/src/main/resources/static/bootstrap/assets/uploads";

    private final AmazonS3 amazonS3;

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

    private String folderName = "profile_imgs/";

    private final UserService userService;

    @GetMapping("/addinfo")
    public String calladdinfo(Model model, HttpSession session) throws Exception {
        User newUser = (User) session.getAttribute("logined");
        String userId = newUser.getUser_id();
        User userinfo = userService.findById(userId);
        model.addAttribute("userinfo",userinfo);
        return "addinfo";
    }


    @PostMapping(value="/addinfo", consumes = "multipart/form-data")
    public String processAddInfo(@ModelAttribute("userInfo") UserRequestDto userDto, @RequestParam("img") MultipartFile file, HttpSession session, RedirectAttributes attributes) throws Exception {
        User loginedUser = (User) session.getAttribute("logined");
        // 업데이트된 정보로 사용자 정보 업데이트
        loginedUser = userService.updateUserInfo(loginedUser.getUser_id(), userDto.getFull_name(), userDto.getGithub(), userDto.getBlog());

        String fileName = null;
        // Check if file is empty
        if (file.isEmpty()) {
            attributes.addFlashAttribute("message", "Please select a file to upload.");
            session.setAttribute("logined", loginedUser);
        } else {
            fileName = file.getOriginalFilename();

            // Path path = Paths.get(UPLOAD_DIR, fileName);

            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(file.getSize());
            metadata.setContentType(file.getContentType());

            // 폴더명을 포함한 객체 키 설정
            String key = folderName + fileName;

            amazonS3.putObject(bucket, key, file.getInputStream(), metadata);

/*
            // Save the file on server
            try {
                Files.createDirectories(path.getParent());
                Files.copy(file.getInputStream(), path);
            } catch (IOException e) {
                e.printStackTrace();
            }

 */
            String imgUrl = amazonS3.getUrl(bucket, key).toString();
            loginedUser = userService.updateUserImage(loginedUser.getUser_id(), imgUrl);
            session.setAttribute("logined", loginedUser);
        }

        return "redirect:/index";
    }

}

IndexFrontController

@Controller
public class IndexFrontController {
    private final UserService userService;

    private HttpSession httpSession;

    private final AmazonS3 amazonS3;

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

    public IndexFrontController(UserService userService, HttpSession httpSession, AmazonS3 amazonS3) {
        this.userService = userService;
        this.httpSession = httpSession;
        this.amazonS3 = amazonS3;
    }
    @GetMapping("/index")
    public String getUserInfo(Model model) throws Exception {
        User newUser = (User) httpSession.getAttribute("logined");
        if (newUser!=null){
            String userId = newUser.getUser_id();

            User user = userService.findById(userId);

            /*
            // 이미지 파일 경로 설정
            String url = amazonS3.getUrl(bucket, filename).toString();

            String imgPath = user.getImage() != null ? "/bootstrap/assets/uploads/" + user.getImage() : "";

            user.setImage(imgPath);
             */

            String fileName = user.getImage();
            if (fileName != null && !fileName.isEmpty()) {
                user.setImage(fileName);
            }

            model.addAttribute("user",user);
            return "index";
        }
        else{
            return "unloginedindex";
        }

    }
}

index와 같은 로직으로 project 코드들도 수정하였다.

ProjectFrontController

@Controller
@RequestMapping("/projects")
public class ProjectFrontController {
    //private static String UPLOAD_DIR = System.getProperty("user.dir") + "/src/main/resources/static/bootstrap/assets/project_uploads";

    private final AmazonS3 amazonS3;

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

    private String folderName = "project_imgs/";
    private final ProjectService projectService;

    public ProjectFrontController(AmazonS3 amazonS3, ProjectService projectService) {
        this.amazonS3 = amazonS3;
        this.projectService = projectService;
    }

    @GetMapping()
    public  String getProject(Model model, HttpSession session) {
        if (session.getAttribute("logined") != null) {
            List<ProjectDto> projects = projectService.findByUserId();

            for (ProjectDto projectDto : projects) {
                String imgPath = projectDto.getImg() != null ? projectDto.getImg() : "";
                projectDto.setImg(imgPath);
            }

            Collections.reverse(projects);
            model.addAttribute("projects", projects);

            return "projects";
        } else {
            return "needlogin";
        }
    }

    @PostMapping(value="/add", consumes = "multipart/form-data")
    public String addProject(@ModelAttribute("project") ProjectFrontRequestDto projectFrontRequestDto, @RequestParam("img") MultipartFile file) throws Exception {
        String fileName = null;
        ProjectRequestDto projectRequestDto = new ProjectRequestDto();

        projectRequestDto.setName(projectFrontRequestDto.getName());
        projectRequestDto.setRole(projectFrontRequestDto.getRole());
        projectRequestDto.setDescription(projectFrontRequestDto.getDescription());
        projectRequestDto.setSkill_list(projectFrontRequestDto.getSkill_list());
        projectRequestDto.setGithub(projectFrontRequestDto.getGithub());
        projectRequestDto.setStart_date(projectFrontRequestDto.getStart_date());
        projectRequestDto.setEnd_date(projectFrontRequestDto.getEnd_date());

        if(!file.isEmpty()) {
            fileName = file.getOriginalFilename();

            /*
            // Normalize the file path
            Path path = Paths.get(UPLOAD_DIR, fileName);

            // Save the file on server
            try {
                Files.createDirectories(path.getParent());
                Files.copy(file.getInputStream(), path);
            } catch (IOException e) {
                e.printStackTrace();
            }
             */
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(file.getSize());
            metadata.setContentType(file.getContentType());

            // 폴더명을 포함한 객체 키 설정
            String key = folderName + fileName;

            amazonS3.putObject(bucket, key, file.getInputStream(), metadata);
            String imgUrl = amazonS3.getUrl(bucket, key).toString();
            projectRequestDto.setImg(imgUrl);
        }

        projectService.createProject(projectRequestDto);
        return "redirect:/projects";
    }
    
    @PostMapping(value="/update", consumes = "multipart/form-data")
    public String updateProject(@ModelAttribute("project") ProjectFrontUpdateReqDto projectReqDto, @RequestParam("img") MultipartFile file) throws Exception {
        System.out.println("hihi");
        String fileName = null;
        ProjectDto projectDto1 = new ProjectDto();

        projectDto1.setProject_id(projectReqDto.getProject_id());
        projectDto1.setName(projectReqDto.getName());
        projectDto1.setRole(projectReqDto.getRole());
        projectDto1.setDescription(projectReqDto.getDescription());
        projectDto1.setSkill_list(projectReqDto.getSkill_list());
        projectDto1.setGithub(projectReqDto.getGithub());
        projectDto1.setStart_date(projectReqDto.getStart_date());
        projectDto1.setEnd_date(projectReqDto.getEnd_date());


        if(!file.isEmpty()) {
            fileName = file.getOriginalFilename();
/*
            // Normalize the file path
            Path path = Paths.get(UPLOAD_DIR, fileName);

            // Save the file on server
            try {
                Files.createDirectories(path.getParent());
                Files.copy(file.getInputStream(), path);
            } catch (IOException e) {
                e.printStackTrace();
            }

 */
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(file.getSize());
            metadata.setContentType(file.getContentType());

            // 폴더명을 포함한 객체 키 설정
            String key = folderName + fileName;

            amazonS3.putObject(bucket, key, file.getInputStream(), metadata);
            String imgUrl = amazonS3.getUrl(bucket, key).toString();
            projectDto1.setImg(imgUrl);
        }
        projectService.updateProject(projectDto1);

        return "redirect:/projects"; // Redirect to avoid form resubmission
    }
}