云端数据与数据库数据不同步解决(定时删除垃圾文件)

解决思路:

  1. 图片上传云端后将图片上传后的路径添加到Redis数据库,用Set类型存储
//此处使用的是阿里云
@PostMapping("/upload")
    public Result upload(MultipartFile imgFile) {
        log.info("文件上传:{}",imgFile);
        try {
            //获取文件原始名
            String originalFilename = imgFile.getOriginalFilename();
            //获取文件后缀名
            String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
            //生成新文件名  UUID是获取一个随机字符串
            String objectName = UUID.randomUUID() + extension;
            //文件请求路径
            String filePath = aliOssUtil.upload(imgFile.getBytes(), objectName);
            //保存路径到Redis,方便后面对垃圾文件进行处理
            redisTemplate.opsForSet().add("imgOnAliOSS",filePath);
            return new Result(true, MessageConstant.UPLOAD_SUCCESS, filePath);

        } catch (IOException e) {
            log.error("文件上传失败:{}",e.getMessage());
        }
        return new Result(false, MessageConstant.PIC_UPLOAD_FAIL);
    }
  1. 套餐数据进行增、改操作时添加路径信息到Redis另一个Set类型存储,删除时将路径信息从Redis删除
    @Transactional	//开启事务
    public void addSetmeal(Setmeal setmeal) {
        //新增套餐
        setmealMapper.insert(setmeal);
        //添加新增图片地址到Redis库存储     key           value
        redisTemplate.opsForSet().add("imgOnLocal", setmeal.getImg());
    }
    @Transactional
    public void updateSetmeal(Setmeal setmeal) {
        //更新检查组
        setmealMapper.update(setmeal);
        //添加新增图片地址到Redis库存储    key             value
        redisTemplate.opsForSet().add("imgOnLocal", setmeal.getImg());
       
    }
    @Override
    @Transactional
    public void delete(Long id) {
    	//查找数据库获取图像信息
        Setmeal setmeal = setmealMapper.getById(id);
        //删除Redis数据库中存储的图片信息      key       value
        redisTemplate.opsForSet().remove("imgOnLocal",setmeal.getImg());
        //删除套餐
        setmealMapper.deleteById(id);
    }
  1. 通过SpringTask定时校验云端是否存在垃圾数据,存在就调用阿里云工具类删除,同时删除云端垃圾文件在Redis数据库中存储信息。
@Slf4j
@Component
public class RedisTask {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private AliOssUtil aliOssUtil;
    @Scheduled(cron = "0 0 1 * * ?")
    public void deleteImgFile() {
        log.info("定时清理垃圾文件");	//输出日志信息
        //获取Redis中Set类型的对象
        SetOperations setOperations = redisTemplate.opsForSet();
        //获取数据库数据和云端数据交集
        Set<String> intersect = setOperations.intersect("imgOnAliOSS", "imgOnLocal");
        //创建新的Redis表存储交集数据
        for (String value : intersect) {
            setOperations.add("intersect", value);
        }
        //获取云端多余数据
        Set<String> difference = setOperations.difference("imgOnAliOSS", "intersect");
        if (difference != null && difference.size() > 0) {
            for (String filePath : difference) {
            	//我的项目中Redis存储信息和阿里云工具类中需要的信息不符合,用split()函数更改一下
                String[] split = filePath.split("/");
                //删除云端上的多余数据
                aliOssUtil.delete(split[3]);
                //清楚Redis数据库中存储的云端多余信息,防止下次运行时出错
                setOperations.remove("imgOnAliOSS",filePath);
            }
        }
        redisTemplate.delete("intersect");
    }
}

用到的技术:

Redis:

Redis是一个内存存储的数据结构数据库,可以用来当数据库、高速缓存和消息队列代理,数据类型有字符串、哈希表、列表、集合、有序集合等,我这里使用的是SpringDataRedis, 在SpringBoot项目中可以通过配置pom.xml文件添加依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

并在application.yml文件中配置自定义的信息

  redis:
    host: localhost 		#服务器
    port: 6379				#端口号
    password: root  		#看你个人有没有设置密码,没有不用配置
    database: 10			#数据库

然后编写配置类,创建RedisTemplate对象(这一步操作中也会修改一下Redis key的序列化器,最后就可以通过RedisTemplate对象操作Redis数据库)

@Configuration
@Slf4j
public class RedisConfiguration {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        log.info("开始创建redis模板对象...");
        RedisTemplate redisTemplate = new RedisTemplate();
        //设置redis的连接工厂对象
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置redis key的序列化器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

注意事项:在项目中使用Redis数据库时数据库必须启动,不然没法用(踩过很多次这个坑)

SpringTask

SpringTask: 多用来设置定时任务:定时获取信息、发送信息、执行操作等,首先在SpringBoot项目的启动类上添加注解@EnableScheduling,作用是开启相关的定时任务功能。

@SpringBootApplication
@EnableScheduling   //开启定时任务
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

然后创建定时任务

    @Scheduled(cron = "0/5 * * * * ?")
    public void sendMessageToClient() {
        System.out.println("Hello");
    }

定时任务方法上需要添加@Scheduled(cron = "0 * * * * ?")注解并指定执行策略,执行策略有:cornfixedDelayflixedRate三种种执行策略.
其中cron表达式(上面用的就是),对应分别是 秒 分 时 日 月 周 (还有年,但这里没用)。cron表达式可以通过在线工具直接生成。

fixedDelay间隔时间是上次任务结束时才开始计时,fixedRate规定了两次定时任务执行的时间间隔,理论上当上一个定时任务开始执行时,下一个定时任务开始时间就已经确定了。(这两种我目前没使用过)
还有一个属性叫做:initialDelay用来初始化延迟时间,也就是第一次延迟执行的时间。只能配合 fixedDelay 或 fixedRate 使用。如 @Scheduled(initialDelay=5000,fixedDelay = 1000)
注意事项:SpringTask默认单线程执行,多线程时会影响到定时策略。SpringTask默认不支持分布式。


关于SpringTask更详细的内容可以看傲娇鹿先生的文章—Spring Boot 中使用 Spring Task 实现定时任务