Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/com/pickyfy/pickyfy/PickyfyApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableJpaAuditing
@EnableScheduling
public class PickyfyApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,15 @@ public interface PlaceImageRepository extends JpaRepository<PlaceImage, Long> {
List<String> findAllByPlaceId(@Param("placeId") Long placeId);

List<PlaceImage> findALlByPlace(Place place);

@Query("""
SELECT url FROM (
SELECT pi.url as url FROM PlaceImage pi
UNION
SELECT u.profileImage as url FROM User u WHERE u.profileImage IS NOT NULL
UNION
SELECT m.iconUrl as url FROM Magazine m WHERE m.iconUrl IS NOT NULL
) urls
""")
List<String> findAllImageUrls();
}
70 changes: 70 additions & 0 deletions src/main/java/com/pickyfy/pickyfy/service/ImageCleanupService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.pickyfy.pickyfy.service;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.ListObjectsV2Request;
import com.amazonaws.services.s3.model.ListObjectsV2Result;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.pickyfy.pickyfy.repository.PlaceImageRepository;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class ImageCleanupService {

private final AmazonS3Client amazonS3Client;
private final PlaceImageRepository placeImageRepository;

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

@Value("${cloud.aws.s3.path.image}")
private String imageFolder;

@Scheduled(cron = "0 0 2 * * 1") // 매주 월요일 새벽 2시에 실행
public void cleanupOrphanedImages() {
try {
// 1. S3의 모든 이미지 목록 가져오기
ListObjectsV2Request listReq = new ListObjectsV2Request()
.withBucketName(bucket)
.withPrefix(imageFolder);

List<String> s3ImageUrls = new ArrayList<>();
ListObjectsV2Result result;

do {
result = amazonS3Client.listObjectsV2(listReq);
for (S3ObjectSummary obj : result.getObjectSummaries()) {
s3ImageUrls.add(amazonS3Client.getUrl(bucket, obj.getKey()).toString());
}
listReq.setContinuationToken(result.getNextContinuationToken());
} while (result.isTruncated());

// 2. DB에 저장된 모든 이미지 URL 가져오기
List<String> dbImageUrls = placeImageRepository.findAllImageUrls();

// 3. S3에는 있지만 DB에는 없는 이미지 찾기
List<String> orphanedImages = s3ImageUrls.stream()
.filter(s3Url -> !dbImageUrls.contains(s3Url))
.toList();

// 4. 고아 이미지 삭제
for (String imageUrl : orphanedImages) {
String fileName = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);
amazonS3Client.deleteObject(bucket, imageFolder + fileName);
log.info("Deleted orphaned image: {}", imageUrl);
}

log.info("Image cleanup completed. Deleted {} orphaned images", orphanedImages.size());

} catch (Exception e) {
log.error("Error during image cleanup", e);
}
}
}