最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot刪除清理垃圾文件的實(shí)現(xiàn)

 更新時(shí)間:2025年10月30日 11:10:01   作者:CyberShen  
本文主要介紹了SpringBoot刪除清理垃圾文件的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、什么是垃圾文件?

垃圾文件是指,在表單中用戶上傳了文件到服務(wù)器,但表單未被提交或保存,導(dǎo)致上傳的這個(gè)文件將不會(huì)被記錄到相關(guān)業(yè)務(wù)表中,再也不會(huì)被使用,成為了垃圾文件。時(shí)間久了服務(wù)器上會(huì)有越來越多無用的垃圾文件占用服務(wù)器存儲(chǔ)。

二、我處理垃圾文件的方式

對(duì)于垃圾文件,一般可配合緩存+事件監(jiān)聽進(jìn)行刪除;還有一種方式是定時(shí)任務(wù),定時(shí)掃表,掃描每一個(gè)使用文件資源的表字段,匯總成在使用的文件,再對(duì)比文件表,即可篩選出需要?jiǎng)h除的垃圾文件。本文中我將介紹兩種方式:

  • Redis+事件監(jiān)聽器
  • 定時(shí)掃表求差集刪除文件

方式一、Redis+事件監(jiān)聽器

當(dāng)文件通過上傳接口上傳到服務(wù)器時(shí),應(yīng)在上傳接口將fileUrl存入Redis中并設(shè)置過期事件。

當(dāng)提交表單時(shí),在ServiceImpl層方法中使用方法進(jìn)行驗(yàn)證,如果圖片在緩存中有,則說明圖片是在有效期內(nèi)提交的,直接從緩存中刪除對(duì)應(yīng)的key即可。這里我設(shè)計(jì)一個(gè)工具類AsyncFileHandler,checkValid()方法一次性可傳入多個(gè)字段進(jìn)行fileUrl校驗(yàn)。當(dāng)然這只能刪除新增時(shí)上傳不提交表單產(chǎn)生的垃圾文件,如果是更新時(shí),舊文件被替換了,提交表單時(shí)也不會(huì)知道舊文件是誰,這時(shí)有朋友可能會(huì)說了:“前端文件組件在刪除文件時(shí)對(duì)應(yīng)的把文件也從服務(wù)器刪除不就好了嘛。”這么說其實(shí)也可以,但是針對(duì)富文本情況呢?富文本里上傳了圖片就沒法再通過上傳組件刪除了。不過別慌,我們可以在提交表單時(shí)查詢出舊數(shù)據(jù),讓新舊數(shù)據(jù)字段進(jìn)行對(duì)比,如果舊字段中的fileUrl未出現(xiàn)在新字段中,則應(yīng)刪除舊字段的fileUrl。當(dāng)然,在更新業(yè)務(wù)中也需要執(zhí)行checkValid()方法校驗(yàn)新上傳文件的有效性,再執(zhí)行compareField()方法檢測(cè)舊文件是否還被使用,不再使用則刪除。

那么,redis中過期的文件怎么處理呢?這需要定義一個(gè)過期事件監(jiān)聽器,當(dāng)key快過期時(shí)觸發(fā)監(jiān)聽器,我們通過監(jiān)聽器拿到redis key刪除未使用的垃圾文件即可。key保存的是fileUrl

1.將上傳的文件返回的fileUrl存入Redis并設(shè)置過期時(shí)間

    String fileUrl = this.storageFile(engine, file, false);     //上傳minio并返回fileUrl
    String redisKey = "file:url:" + fileUrl;                    
    cacheOperator.put(redisKey, fileUrl, 60 * 60 * 24);         //將fileUrl存入Redis中并設(shè)置24小時(shí)過期

2.定義AsyncFileHandler類,方便檢測(cè)文件有效性和對(duì)比新文件刪除舊文件。

@Slf4j
 @Service
 @EnableAsync
 @Component
 public class AsyncFileHandler {

     @Resource
     private CommonCacheOperator cacheOperator;

     @Resource
     private DevFileApi devFileApi;

     // URL正則表達(dá)式模式
     private static final Pattern URL_PATTERN = Pattern.compile("/(\\d+)\\.[a-zA-Z0-9]+");

     @Async
     public void checkValid(String... imgFields) throws IOException {
         for (String field : imgFields) {
             Matcher matcher = URL_PATTERN.matcher(field);
             Set<String> extractedUrls = new HashSet<>();
             while (matcher.find()) {
                 String fileUrl = matcher.group(1);
                 extractedUrls.add(fileUrl);
             }
             // 刪除Redis中匹配的URL
             deleteMatchedUrlsFromRedis(extractedUrls);
         }
     }

     // 比較新舊記錄字段中文件的差異,并刪除未被使用的舊字段
     @Async
     public void compareField(String newFiled, String oldFiled){
         Matcher matcher1= URL_PATTERN.matcher(newFiled);
         Matcher matcher2= URL_PATTERN.matcher(oldFiled);
         Set<String> newFileUrls = new HashSet<>();
         Set<String> oldFileUrls = new HashSet<>();
         while (matcher1.find()) {
             String fileUrl = matcher1.group(1);
             newFileUrls.add(fileUrl);
         }
         while (matcher2.find()) {
             String fileUrl = matcher2.group(1);
             oldFileUrls.add(fileUrl);
         }
         Set<String> toDelFileUrls = oldFileUrls.stream()
                 .filter(element -> !newFileUrls.contains(element))
                 .collect(Collectors.toSet());
         // 刪除不再使用的文件
         deleteDiffFileUrls(toDelFileUrls);
     }
     
     // 刪除在有效期內(nèi)提交表單的文件,不刪除的話后面會(huì)在redis過期事件中被刪除。
     private void deleteMatchedUrlsFromRedis(Set<String> urls) {
         int deletedCount = 0;
         for (String url : urls) {
             String redisKey = "file:url:" + url;
             if (cacheOperator.get(url)!=null){
                 cacheOperator.remove(redisKey);
                 log.info("從Redis中刪除已引用的文件URL: {}", url);
                 deletedCount++;
             }
         }
         log.info("共處理{}個(gè)文件URL,成功刪除{}個(gè)Redis鍵", urls.size(), deletedCount);
     }
     
     // 刪除新舊差異文件
     private void deleteDiffFileUrls(Set<String> urls) {
         for (String url : urls) {
             devFileApi.deleteFileByUrl(url);
         }
     }
}

3.定義Redis過期事件監(jiān)聽器

@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {

   @Resource
   private DevFileApi devFileApi;

   /**
    * 創(chuàng)建RedisKeyExpirationListener bean時(shí)注入 redisMessageListenerContainer
    *
    * @param redisMessageListenerContainer RedisConfig中配置的消息監(jiān)聽者容器bean
    */
   public RedisKeyExpirationListener(RedisMessageListenerContainer redisMessageListenerContainer) {
       super(redisMessageListenerContainer);
   }

   @Override
   public void onMessage(Message message, byte[] pattern) {
       String channel = new String(message.getChannel()); // __keyevent@*__:expired
       String pa = new String(pattern); // __keyevent@*__:expired
       String expiredKey = message.toString();
       if (expiredKey.startsWith("Cache:file:url:")){
           System.out.println("監(jiān)聽到過期文件:" + expiredKey);
           devFileApi.deleteFileByUrl(expiredKey);
       }
   }
}

方式二、定時(shí)掃表求差集刪除文件

一個(gè)系統(tǒng)中可能好幾個(gè)表中的多個(gè)字段使用文件資源,一個(gè)情況方式一也可以解決。假設(shè)有這樣一種情況:系統(tǒng)中的一個(gè)文件可能被多個(gè)表的字段共享,表中的字段存入的是同一個(gè)fileUrl,如果刪除這個(gè)文件可能會(huì)牽扯到好幾張表。這種情況就適合掃表求差集刪除文件了。首先找出全數(shù)據(jù)庫表中使用文件的字段,找出這些字段使用的fileUrl,肯定有重復(fù)的fileUrl,所以我們使用Set集合保存,多個(gè)fileUrl只保留一個(gè)即可,這個(gè)Set集合里存的就是我們數(shù)據(jù)庫中所有在使用的文件,非垃圾文件,那么直接在文件表中使用notIn查詢,查詢出不在使用的文件,也就是垃圾文件即可,然后刪除這些垃圾文件。直接上代碼:

/**
 * 清理垃圾文件定時(shí)任務(wù)
 */
@Slf4j
@Component
public class ClearGarbageFileTaskRunner implements CommonTimerTaskRunner {

    @Resource
    private DevFileService devFileService;

    @Override
    public void action(String extJson) {
        // 哪個(gè)表哪個(gè)字段在使用文件資源
        Map<String, String[]> map = new HashMap<>();
        map.put("BIZ_CHILD_USER", new String[]{"AVATAR"});
        map.put("BIZ_COLLECT", new String[]{"ANALYSIS_VIDEO","ANALYSIS", "COVER", "OPTIONS", "CONTENT"});
        map.put("BIZ_QUESTION", new String[]{"ANALYSIS_VIDEO", "ANALYSIS","COVER", "OPTIONS", "CONTENT"});
        map.put("BIZ_WRONG", new String[]{"ANALYSIS_VIDEO","ANALYSIS", "COVER", "OPTIONS", "CONTENT"});
        map.put("BIZ_EXAM_RECORD", new String[]{"ANALYSIS_VIDEO","ANALYSIS", "COVER", "OPTIONS", "CONTENT"});
        map.put("BIZ_LECTURE", new String[]{"VIDEO", "INTRODUCE"});
        map.put("BIZ_NOTICE", new String[]{"IMAGE", "CONTENT"});

        // 全表中在使用的文件ID集合
        Set<String> allTableInUseFileIdSet = new HashSet<>();
        for (Map.Entry<String, String[]> entry : map.entrySet()) {
            Set<String> oneTableInUseFileIdSet = devFileService.getInUseFileIdList(entry.getKey(), entry.getValue());
            allTableInUseFileIdSet.addAll(oneTableInUseFileIdSet);
        }
        log.info("------------------------------------------------清理垃圾文件Start------------------------------------------------");
        log.info("在使用的文件數(shù)量:{}", allTableInUseFileIdSet.size());
        // 獲取垃圾文件ID
        List<DevFileIdParam> junkFileIds = devFileService.list(new LambdaQueryWrapper<DevFile>().notIn(DevFile::getId, allTableInUseFileIdSet))
                .stream().map(devFile -> {
                    DevFileIdParam param = new DevFileIdParam();
                    param.setId(devFile.getId()); // 確保字段名稱匹配
                    return param;
                }).toList();
        log.info("待清理的文件數(shù)量:{}", junkFileIds.size());
        if (!junkFileIds.isEmpty()) {
            // 執(zhí)行物理刪除垃圾文件
            devFileService.deleteGarbageFiles(junkFileIds);
        }
        log.info("------------------------------------------------清理垃圾文件End------------------------------------------------\n");
    }
}

注意:方式二刪除垃圾文件,一定不要遺漏需要過濾的數(shù)據(jù)表字段,否則會(huì)被誤刪除。

到此這篇關(guān)于SpringBoot刪除清理垃圾文件的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot刪除清理垃圾文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

金寨县| 永嘉县| 昭苏县| 新化县| 隆昌县| 丹江口市| 田东县| 黔西| 青龙| 呼伦贝尔市| 石柱| 辽源市| 天水市| 仁怀市| 东明县| 依兰县| 师宗县| 保康县| 屯留县| 新津县| 安吉县| 渭源县| 郸城县| 涿鹿县| 镇宁| 南乐县| 乌鲁木齐县| 肇州县| 塔城市| 张掖市| 长岭县| 招远市| 罗平县| 台北市| 集贤县| 随州市| 屯昌县| 甘德县| 满城县| 松溪县| 神农架林区|