프로그래밍/백엔드

Spring 환경에서 네이버 클라우드 플랫폼(NCP) 파일서버 연동 구현 (Feat. AWS S3)

제로스크랩 2024. 4. 20. 19:12

Azure에서 NCP으로 교체하는 요청이 있었다.

파일서버 연동은 처음이라 공식홈페이지의 샘플코드를 보면서 작업을 했지만 도통 뭔소린지 알 수 없어 꽤나 걱정이 많았던 작업이었다.

약 1주정도 무한 구글링을 하면서 필요한 지식들을 조각조각 모아서 테스트를 성공적으로 마쳤다.

 

너무 큰 고통을 받고나니 Spring과 AWS에 대한 공부가 많이 필요하다는 것을 느꼈다.

 

java 17
Spring boot 3.2.x

 

1. build.gradle 설정

implementation 'com.amazonaws:aws-java-sdk-s3:1.12.638'

 

2. 클래스 구현

package com.org.mgt.common.util;

import com.amazonaws.SdkClientException;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.util.IOUtils;
import com.org.mgt.common.vo.FileDTVO;
import com.org.mgt.common.vo.FileHTVO;
import com.org.mgt.common.vo.UserVO;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@Component
public class NcpStorage {

  private final Environment env;
  private String endPoint = "https://kr.object.ncloudstorage.com";
  private  String regionName = "kr-standard";
  private  String accessKey = "";
  private  String secretKey = "";
  private  String bucketName = "";
  private String root = "";
  private AmazonS3 s3 = null;

  public NcpStorage(Environment env) {
    this.env = env;
    this.accessKey = env.getProperty("ncp.accessKey");
    this.secretKey = env.getProperty("ncp.secretKey");
    this.bucketName = env.getProperty("ncp.bucketName");
    //wiselection-bucket.kr.object.ncloudstorage.com/
    this.root = env.getProperty("ncp.root");

    // S3 client
    this.s3 = AmazonS3ClientBuilder.standard()
            .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, regionName))
            .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
            .build();
  }

  /**
   * 파일 업로드
   */
  public FileDTVO uploadFile(
          HttpServletRequest request,
          Environment env,
          FileHTVO fileHTVO,
          MultipartFile multipartFile
  ){
    FileDTVO fileDTVO = new FileDTVO();
    fileDTVO.setResultCode("OK");

    try {
      if (multipartFile != null){
        Date date = new Date();
        SimpleDateFormat sdf= new SimpleDateFormat("yyyyMMddmmssSSS", Locale.KOREA);
        String fileDate = sdf.format(date);

        //확장자
        int pos = multipartFile.getOriginalFilename().lastIndexOf(".");
        String ext = multipartFile.getOriginalFilename().substring(pos + 1);

        String filePath = fileHTVO.getFileStoreLocation();
        String fileName = multipartFile.getOriginalFilename();
        String key = filePath + "/" + fileName;

        File file = new File(filePath);
        if (!file.exists()) {
          file.mkdirs();
        }
        file = new File(key);
        fileDTVO.setFileStoreLocation(filePath);

        UserVO loginUser = (UserVO) request.getSession().getAttribute("loginUser");

        HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String ip = req.getHeader("X-FORWARDED-FOR");
        if (ip == null) {
          ip = req.getRemoteAddr();
        }

        if (loginUser != null) {
          fileDTVO.setRegIdx(loginUser.getUserId());
        }
        fileDTVO.setRegIp(ip);

        // 네이버 클라우드 플랫폼 Upload 시작
        String folderName = filePath;
        String objectName = folderName + "/" + fileName;
        String fileNo = "";

//        String fileUrl = createBucketFileUrl();
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(multipartFile.getSize());
        objectMetadata.setContentType(multipartFile.getContentType());

        PutObjectRequest putObjectRequest = new PutObjectRequest(
                bucketName,
                objectName,
                multipartFile.getInputStream(),
                objectMetadata
        ).withCannedAcl(CannedAccessControlList.PublicRead);

        fileNo = putObjectRequest.getKey();

        try {
          s3.putObject(putObjectRequest);
        } catch (AmazonS3Exception e) {
          e.printStackTrace();
        } catch(SdkClientException e) {
          e.printStackTrace();
        }
        // 네이버 클라우드 플랫폼 Upload 끝

        fileDTVO.setFileNo(fileHTVO.getFileNo()); // fileNo
        fileDTVO.setOriginFileNm(fileName); // name
        fileDTVO.setStoreFileNm(key);
        fileDTVO.setFileStoreCours(env.getProperty("ncp.root") + "/" + key); // full URL
        fileDTVO.setFileExtsn(ext);

        fileDTVO.setFileSize(multipartFile.getSize());
      }

    } catch (Exception e) {

      e.printStackTrace() ;
      System.out.println( e.getMessage());
    }

    return fileDTVO;
  }

  /**
   * 파일 다운로드
   */
  public ResponseEntity<byte[]> downloadFile(String fileName, String orgFileName, String fileStoreCours) {
    byte[] bytes = null;
    HttpHeaders headers = null;
    String objectName = fileName;
    String downloadFilePath = fileStoreCours;

    try {
      S3Object s3Object = s3.getObject(bucketName, objectName);
      S3ObjectInputStream s3ObjectInputStream = s3Object.getObjectContent();
      bytes = IOUtils.toByteArray(s3ObjectInputStream);

      headers = new HttpHeaders();
      headers.setContentType(MediaType.IMAGE_PNG);
      headers.setContentDisposition(ContentDisposition.builder("attachment")
              .filename(URLEncoder.encode(orgFileName,"UTF-8")).build());
    } catch (IOException e) {
      e.printStackTrace();
    }

    return new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
  }

  /**
   * 파일목록 단건삭제
   */
  public void deleteFileOne(String objectName) throws Exception {
    try {
      s3.deleteObject(bucketName, objectName);
      System.out.format("Object %s has been deleted.\n", objectName);
    } catch (AmazonS3Exception e) {
      e.printStackTrace();
    } catch(SdkClientException e) {
      e.printStackTrace();
    }
  }

  /**
   * 파일목록 전체삭제
   */
  public void deleteFileAll() throws Exception {

    try {
      // list all in the bucket
      ListObjectsRequest listObjectsRequest = new ListObjectsRequest()
              .withBucketName(bucketName)
              .withMaxKeys(300);

      ObjectListing objectListing = s3.listObjects(listObjectsRequest);

      while (true) {
        for (S3ObjectSummary objectSummary : objectListing.getObjectSummaries()) {
          log.info("### url: {}", root + objectSummary.getKey());
          log.info("### url: {}", root + objectSummary.getSize());
          log.info("### url: {}", root + objectSummary.getOwner().getId());

          deleteFileOne(objectSummary.getKey());
        }

        if (objectListing.isTruncated()) {
          objectListing = s3.listNextBatchOfObjects(objectListing);
        } else {
          break;
        }
      }
    } catch (AmazonS3Exception e) {
      System.err.println(e.getErrorMessage());
      System.exit(1);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * 파일목록 조회
   */
  public ArrayList<Map<String, Object>> selectFileInfo(String key) {
    ArrayList<Map<String, Object>> resultList = new ArrayList<>();
    Map<String, Object> resultMap = new HashMap<>();

    try {
      // list all in the bucket
      ListObjectsRequest listObjectsRequest = new ListObjectsRequest()
              .withBucketName(bucketName)
              .withMaxKeys(300);

      ObjectListing objectListing = s3.listObjects(listObjectsRequest);

      while (true) {
        for (S3ObjectSummary objectSummary : objectListing.getObjectSummaries()) {
          if(key.equals(objectSummary.getKey())){
            log.info("### url: {}", root + "/" + objectSummary.getKey());
            resultMap.put("fileNo", objectSummary.getKey());
            resultMap.put("url", root + "/" + objectSummary.getKey());
            resultMap.put("size", objectSummary.getSize());

            resultList.add(resultMap);
          }
        }

        if (objectListing.isTruncated()) {
          objectListing = s3.listNextBatchOfObjects(objectListing);
        } else {
          break;
        }
      }
    } catch (AmazonS3Exception e) {
      System.err.println(e.getErrorMessage());
      System.exit(1);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    return resultList;
  }

//  private String createBucketFileUrl() {
//    // 버킷 파일의 URL을 생성
//    UUID key = UUID.randomUUID();
//    String fileUrl = s3.getUrl(bucketName, String.valueOf(key)).toString();
//
//    return fileUrl;
//  }
}
728x90
반응형