https://ckeditor.com/ckeditor-4/download/
여러 시행착오를 겪으며 코드를 짰는데 완벽하지는 않다.
우선, 제일 큰 단점.. S3 서버에 이미지 파일 업로드를 하긴 하는데 CKEditor4에 다시 뿌려지는 이미지를 제거해도 서버 리소스가 삭제되지 않는다 (;^◇^;)ゝ
ㅋ.ㅋ..
이건 CKEditor4를 자세히 뜯어봐야할 것 같은데, 여전히 할 게 산더미인 관계로 나중에 다시 고민해보기로 했다.!
지난번에 appliation.properties
에 이미지 패스 설정을 해줬었는데, 여기에 그대로 추가하면 된다.
(이전 코드)
#FILE_UPLOAD
resource.handler = /ck/images/**
resource.location = file:///C:/2023_side/imgUpload/
image.upload.path = C:/2023_side/imgUpload
⬇ ⬇
(이렇게 변경!)
resource.handler = /upload-image/**
#S3_IAM
cloud.aws.credentials.accessKey= {IAM 계정 Access Key}
cloud.aws.credentials.secretKey= {IAM 계정 Secret Key}
cloud.aws.stack.auto=false
#S3_SERVICE_BUCKET
cloud.aws.s3.bucket={설정한 버킷 이름}
cloud.aws.region.static=ap-northeast-2(한국, 서울)
#S3_BUCKET_URL
cloud.aws.s3.bucket.url=https://{설정한 버킷 이름}.s3.ap-northeast-2.amazonaws.com
AWS S3 사용을 위한 gradle 의존성을 추가해야 한다. 추가하고서 코끼리 아이콘 눌러 재빌드 필수!
implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-aws', version: '2.2.6.RELEASE'
신규 버전 확인은 https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-aws
이 페이지에서 하면 된다.
AwsConfig
라는 클래스를 새로 만들고 아래처럼 코드를 작성한다.
@Configuration
public class AwsConfig {
@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 awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.build();
}
}
마찬가지로, 이전 포스팅에서 한 번 다뤘었는데 핸들링해야하는 경로가 바뀌었으니 다시 설정해야 한다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Value("${resource.handler}")
private String resourceHandler;
@Value("${cloud.aws.s3.bucket.url}")
private String resourceLocation;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(resourceHandler)
.addResourceLocations(resourceLocation);
}
}
resourceHandler
에서 설정한 URL로 요청이 올 경우, resourceLocation
으로 대체되는 로직이다.
이전에는 컨트롤러만 만들었는데 이번에는 서비스를 분리해볼 거다.
@Service
@RequiredArgsConstructor
public class S3FileService {
@Value("${cloud.aws.s3.bucket}")
private String bucketName;
private final AmazonS3 amazonS3;
public String s3FileUpload(MultipartFile multipartFile, String dir) throws IOException {
//파일명 설정 : {UUID}_{경로}.{extension}
String extension = FilenameUtils.getExtension(multipartFile.getOriginalFilename());
String s3FileName = UUID.randomUUID() + "_" + dir + "." + extension;
//파일 저장경로 설정
String uploadFilePath = dir + "/";
//파일 사이즈 설정
ObjectMetadata objMeta = new ObjectMetadata();
objMeta.setContentLength(multipartFile.getSize());
String keyName = uploadFilePath + s3FileName;
//파일 업로드
// 외부에 공개하는 파일인 경우 Public Read 권한을 추가, ACL 확인
amazonS3.putObject(new PutObjectRequest(bucketName, keyName, multipartFile.getInputStream(), objMeta)
.withCannedAcl(CannedAccessControlList.PublicRead));
return amazonS3.getUrl(bucketName, keyName).toString();
}
}
dir
은 말그대로 이미지가 업로드 될 파일 경로 다. CKEditor4 외에도, 자체 프로필 업로드를 구현할 예정이라 확장성을 위해 미리 경로를 분리했다. { UUID }_{ 경로 }.{ extension }
이런 형태이다.@Slf4j
@Controller
@RequestMapping("/boards")
@RequiredArgsConstructor
public class FileUploadController {
@Autowired
S3FileService s3FileService;
@ResponseBody
@PostMapping("/ck/imageUpload.do")
public void ckFileUpload(HttpServletResponse res,
@RequestParam MultipartFile upload) {
log.info("컨 트 롤 러 진 입 ! ! ! !");
PrintWriter printWriter = null;
try {
//s3 파일 업로드
//s3CkUploadPath = {location}/ck/{uid}_ck.{ext}
String s3CkUploadPath = s3FileService.s3FileUpload(upload, "ck");
log.info("s3CkUploadPath : {}", s3CkUploadPath);
//파일 크기
byte[] bytes = upload.getBytes();
//원본 파일명
String originFileName = upload.getOriginalFilename();
//저장 파일명
String savedFileName = s3CkUploadPath.substring(s3CkUploadPath.lastIndexOf("/") + 1);
//ckEditor 로 전송
printWriter = res.getWriter();
String fileUrl = "/upload-image/" + "ck/" + savedFileName;
log.info("fileUrl : {}", fileUrl);
//json 으로 변환
JsonObject json = new JsonObject();
json.addProperty("uploaded", 1);
json.addProperty("fileName", originFileName);
json.addProperty("url", fileUrl);
printWriter.println(json);
printWriter.flush(); //초기화
log.info("json : {}", json);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(printWriter != null) { printWriter.close(); }
}
}
}
s3CkUploadPath
에는 서비스에서 반환된 실제 저장 경로가 담긴다.S3FileDTO
객체를 만들어 모든 정보를 한 번에 반환했으면 더 깔끔했을 것같다.(*☉౪ ⊙。) ..
아무튼, 이 로직은 반환된 URL에서 저장된 이미지 파일명을 분리해주는 로직이다.WebConfig
에서 설정해둔대로 /upload-image/
하위 경로로 오는 요청은 전부 본래 URL로 대체된다.
cke에 s3를 붙이나요? 신기하네용