亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

一、什么是垃圾文件?

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

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

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

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

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

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

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

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

1.將上傳的文件返回的fileUrl存入Redis并設(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小時過期

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

@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)提交表單的文件,不刪除的話后面會在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("共處理{}個文件URL,成功刪除{}個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時注入 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);
       }
   }
}

方式二、定時掃表求差集刪除文件

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

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

    @Resource
    private DevFileService devFileService;

    @Override
    public void action(String extJson) {
        // 哪個表哪個字段在使用文件資源
        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ù)表字段,否則會被誤刪除。

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

相關(guān)文章

  • Mybatis中強(qiáng)大的resultMap功能介紹

    Mybatis中強(qiáng)大的resultMap功能介紹

    這篇文章主要給大家介紹了關(guān)于Mybatis中強(qiáng)大的resultMap功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Mybatis具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • SpringBoot詳細(xì)講解視圖整合引擎thymeleaf

    SpringBoot詳細(xì)講解視圖整合引擎thymeleaf

    這篇文章主要分享了Spring Boot整合使用Thymeleaf,Thymeleaf是新一代的Java模板引擎,類似于Velocity、FreeMarker等傳統(tǒng)引擎,關(guān)于其更多相關(guān)內(nèi)容,需要的小伙伴可以參考一下
    2022-06-06
  • JAVA基本類型包裝類 BigDecimal BigInteger 的使用

    JAVA基本類型包裝類 BigDecimal BigInteger 的使用

    Java 中預(yù)定義了八種基本數(shù)據(jù)類型,包括:byte,int,long,double,float,boolean,char,short,接下來文章小編將向大家介紹其中幾個類型的內(nèi)容,需要的朋友可以參考下文章
    2021-09-09
  • spring boot simple類型cache使用詳解

    spring boot simple類型cache使用詳解

    這篇文章主要介紹了spring boot simple類型cache使用,這里用的不是 redis 的緩存,simple 的緩存默認(rèn)用的是java的ConcurrentHashMap, 單純的simple緩存,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • 解決springcloud Zuul丟失Cookie的問題

    解決springcloud Zuul丟失Cookie的問題

    這篇文章主要介紹了解決springcloud Zuul丟失Cookie的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • SpringBoot如何集成Netty

    SpringBoot如何集成Netty

    這篇文章主要介紹了SpringBoot如何集成Netty問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • JAVA中反射機(jī)制和模塊化的深入講解

    JAVA中反射機(jī)制和模塊化的深入講解

    很多剛學(xué)Java反射的同學(xué)可能對反射技術(shù)一頭霧水,為什么要學(xué)習(xí)反射,學(xué)習(xí)反射有什么作用,下面這篇文章主要給大家介紹了關(guān)于JAVA中反射機(jī)制和模塊化的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • 淺談Spring的兩種配置容器

    淺談Spring的兩種配置容器

    這篇文章主要介紹了淺談Spring的兩種配置容器,介紹了其實(shí)現(xiàn)以及簡單的實(shí)例,具有一定參考價值,需要的朋友可以了解下。
    2017-10-10
  • Java 通過API操作GraphQL

    Java 通過API操作GraphQL

    這篇文章主要介紹了Java 通過API操作GraphQL的方法,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-05-05
  • 解決SpringBoot2.1.0+RocketMQ版本沖突問題

    解決SpringBoot2.1.0+RocketMQ版本沖突問題

    這篇文章主要介紹了解決SpringBoot2.1.0+RocketMQ版本沖突問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06

最新評論