https://sorrel012.tistory.com/309
이전 글에서 CKEditor 통해 S3에 이미지 업로드 하는 것까지 기록하였다.
글의 마지막에 삭제는 별도로 처리해주어야 한다고 했는데, 생각보다 어렵지 않아 바로 구현할 수 있었다.
먼저 해결하고자 했던 문제에 대해서 자세히 설명하자면, CKEditor 를 통해 이미지를 첨부하는 순간 클라우드에 업로드가 되고, 계속 글을 작성하다가 첨부한 사진을 삭제하면 S3에는 사진이 그대로 남아버리게 된다.
이를 해결하기 위해서 첨부한 사진의 URL을 배열에 넣어두고, 최종적으로 글을 등록할 때 서버로 함께 넘겨서 비교하였다.
class MyUploadAdapter {
constructor(loader, uploadUrl, component) {
this.loader = loader;
this.uploadUrl = uploadUrl;
this.component = component;
}
upload() {
return new Promise(async (resolve, reject) => {
const data = new FormData();
const file = await this.loader.file;
data.append('upload', file);
axios.post(this.uploadUrl, data)
.then(response => {
this.component.images.push(response.data.url);
resolve({
default: response.data.url
});
})
.catch(error => {
console.error('File upload error:', error);
reject(error);
});
});
}
}
CKEditor 컴포넌트에 구현한 커스텀 업로드 어댑터이다.
그냥 이미지를 올리기만 할 때는 CKEditor에서 제공해주는 대로 간편하게 쓰면 됐지만, response에서 첨부한 이미지의 url을 받아와야 하기 때문에 어댑터를 별도의 class로 구현해주었다.
mounted() {
const component = this;
const uploadUrl = this.$store.state.url + 'treasure/image';
CustomEditor.create(document.querySelector('#editor'), {})
.then(editor => {
component.editor = editor;
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
return new MyUploadAdapter(loader, uploadUrl, component);
};
editor.setData(component.initialData);
editor.model.document.on('change:data', () => {
component.initialData = editor.getData();
component.$emit('write', component.initialData);
component.$emit('images', component.images);
});
})
.catch(error => {
console.error('There was a problem initializing the editor.', error);
});
},
mounted에서 위에서 구현한 어댑터를 사용해 이미지를 저장하도록 하고, 데이터가 변할 때마다 상위 컴포넌트인 글쓰기 컴포넌트에 본문 내용과 함께 image 배열도 보내준다.
const formData = new FormData();
formData.append('content', this.content);
formData.append('title', this.title);
formData.append('id', sessionStorage.getItem('id'));
formData.append('images', JSON.stringify(this.images));
게시글을 저장할 때 원래는 id까지만 전송했는데, images 배열도 json.stringify 사용 후 전송해준다.
이제 서버에서는 images를 받은 후 List 형식으로 변환해야 한다.
JSONArray jsonArray = new JSONArray(images);
List<String> imageList = new ArrayList<>();
if(jsonArray.length() > 0) {
for(int i = 0; i < jsonArray.length(); i++){
String imageUrl = jsonArray.getString(i);
imageList.add(imageUrl);
}
}
사진을 아예 첨부하지 않았을 수도 있기 때문에 length 조건을 걸어주었다.
//이미지를 업로드 한 적 있는지 확인
if(!imageList.isEmpty()){
//게시글 이미지 추출
List<String> contentImages = new ArrayList<>();
Document doc = Jsoup.parse(writing.getContent());
Elements imageElements = doc.select("img");
for (Element imageElement : imageElements) {
String imageUrl = imageElement.attr("src");
contentImages.add(imageUrl);
}
//업로드 후 삭제한 이미지 추출
List<String> deletedImg = new ArrayList<>(imageList);
deletedImg.removeAll(contentImages);
//AWS S3에서 이미지 삭제
s3Service.deleteImage(deletedImg);
}
이미지를 업로드 한 적이 있을 경우에만 로직을 실행한다.
먼저, 게시글 안에 포함되어 있는 이미지 URL을 추출해 준다.
CKEditor는 html 형식으로 저장되기 때문에 이미지 url을 추출할 때 jsoup 라이브러리를 활용했다.
문자열 함수로 파싱하려면 할 수는 있지만, 더 편하게 라이브러리 활용!
jsoup 라이브러리 사용하려면 pom.xml에 의존성도 추가해 주어야 한다.
<!--Jsoup-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.16.1</version>
</dependency>
* 최신 버전 참고
https://central.sonatype.com/artifact/org.jsoup/jsoup
본문에서 이미지 url들만 골라낸 후에는 첨부한 적이 있는 모든 이미지의 url을 담아둔 List(imageList)와 비교한다.
imageList에는 있지만 본문에서 골라낸 이미지 url에는 해당하지 않는 아이들을 찾으면 삭제된 이미지 url들을 얻을 수 있다.
이제 S3FileUploadService (직접적으로 aws와 연계되는 서비스 클래스)에 삭제 메소드를 구현해주면 된다.
for (String img : deletedImg) {
String decodedUrl = "";
//URL 디코딩
try {
decodedUrl = URLDecoder.decode(img, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String awsUrl = "https://postcard17.s3.ap-northeast-2.amazonaws.com/";
img = decodedUrl.substring(awsUrl.length());
amazonS3.deleteObject(bucket, img);
}
받아온 url들이 인코딩되어 있기 때문에 먼저 디코딩해주고, 디코딩된 url에서 파일명만 추출해준다.
그 파일명을 넘겨주면 바로 삭제 완료~
로직을 생각해 내는 게 좀 오래 걸렸고, 구현 자체는 어렵지 않았다.
사진 두 개를 첨부한 후 다시 하나를 지우고 등록하면?
vue 이미지만 남고 스프링 부트 이미지는 삭제 완료!
'AWS' 카테고리의 다른 글
[AWS] Server Error error: no pg_hba.conf entry for host "", user "", database "", no encryption PostgreSQL 연결 에러 해결 (0) | 2024.03.05 |
---|---|
[AWS] RDS 구축 (0) | 2023.10.16 |
[AWS] EC2 인스턴스 생성 (0) | 2023.10.15 |
[AWS] S3 이미지 업로드(+ CKEditor5) (1) | 2023.10.12 |