Java实现图片上传云存储服务器以及定时清理图片(定时任务组件Qquartz、cron表达式)

Java实现图片上传云存储服务器以及定时清理图片(定时任务组件Qquartz、cron表达式)

前言:

在实际开发中,我们会有很多处理不同功能的服务器。例如:

应用服务器:负责部署我们的应用

数据库服务器:运行我们的数据库

文件服务器:负责存储用户上传文件的服务器

分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率

常见的图片存储方案:

方案一:使用nginx搭建图片服务器

方案二:使用开源的分布式文件存储系统,例如Fastdfs、HDFS等

方案三:使用云存储,例如阿里云、七牛云等

1、七牛云存储

官网地址:https://www.qiniu.com/

自行注册并登录

1.1、新建空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-epbosNqP-1655385753847)(images/image-20220616085739266.png)]

1.2、空间概览

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tzx4CNwh-1655385753848)(images/image-20220616090134747.png)]

文件管理中有个外链域名,很重要后面需它来访问空间上传文件

1.3、开发者中心手册

通过javaSDK链接访问存储空间

文档地址:https://developer.qiniu.com/kodo/1239/java

maven工程使用七牛云需要引入坐标

<dependency>
  <groupId>com.qiniu</groupId>
  <artifactId>qiniu-java-sdk</artifactId>
  <version>[7.7.0, 7.10.99]</version>
</dependency>

如果打包报错提升什么引入什么

<dependencies>
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp</artifactId>
      <version>3.14.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.5</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.qiniu</groupId>
      <artifactId>happy-dns-java</artifactId>
      <version>0.1.6</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

1.4、鉴权

Java SDK的所有的功能,都需要合法的授权。授权凭证的签算需要七牛账号下的一对有效的Access Key和Secret Key,这对密钥可以在七牛云管理控制台的个人中心(https://portal.qiniu.com/user/key)获得,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tzh7rQbL-1655385753848)(images/image-20220616090846238.png)]

1.5、Java SDK操作七牛云之上传

参考官方操作文档本地上传文件

下面的代码是基于新版本的,所以maven需要7.7以上

package com.zcl;

import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.junit.Test;

/**
 * 项目名称:health_parent
 * 描述:七牛云对象存储上传图片
 *
 * @author zhong
 * @date 2022-06-16 9:15
 */
public class qiniuyunTest {
    
    
    // 1、使用七牛云的sdk实现将本地的图片上传到云服务器
    @Test
    public void test() {
    
    
        //构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration(Region.region0());
        //...其他参数参考类注释

        UploadManager uploadManager = new UploadManager(cfg);
        //...生成上传凭证,然后准备上传
        String accessKey = "V5sCLYwDcx5YYWvSzeR9imn7aLZyU22oW0q";
        String secretKey = "1-noh3fPNBcT9rVWiZFIhQ7Atwd_av2VkP9w";
        // 空间名称
        String bucket = "itzcl";
        //如果是Windows情况下,格式是 D:\\qiniu\\test.png
        String localFilePath = "D:\\03a36073-a140-4942-9b9b-712cecb144901.jpg";
        //默认不指定key的情况下,以文件内容的hash值作为文件名
        String key = "测试设定上传文件名称.jpg";

        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);

        try {
    
    
            Response response = uploadManager.put(localFilePath, key, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            System.out.println(putRet.key);
            System.out.println(putRet.hash);
        } catch (QiniuException ex) {
    
    
            Response r = ex.response;
            System.err.println(r.toString());
            try {
    
    
                System.err.println(r.bodyString());
            } catch (QiniuException ex2) {
    
    
                //ignore
            }
        }

    }
}

查看上传的图片文件信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FUgh3ymL-1655385753849)(images/image-20220616093107994.png)]

查看详情可以看到图片

1.5、Java SDK操作七牛云之删除文件

/**
 * 删除七牛云的图片
 */
@Test
public void test2(){
    
    
    //构造一个带指定 Region 对象的配置类
    Configuration cfg = new Configuration(Region.region0());
    //...其他参数参考类注释

    String accessKey = "V5sCLYwDcx5YYWvxKVSzeR9imkaLZyU22oW0q";
    String secretKey = "1-noh3fPNBcT9rVWihXQ7Atwd_avi4f2VkP9w";
    // 空间名称
    String bucket = "itzcl";
    // 指定文件
    String key = "FuM1Sa5TtL_ekLsdkYWcf5pyjKGu";

    Auth auth = Auth.create(accessKey, secretKey);
    BucketManager bucketManager = new BucketManager(auth, cfg);
    try {
    
    
        bucketManager.delete(bucket, key);
    } catch (QiniuException ex) {
    
    
        //如果遇到异常,说明删除失败
        System.err.println(ex.code());
        System.err.println(ex.response.toString());
    }

}

1.6、封装七牛云操作工具类封装

package com.itheima.utils;

import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;

/**
 * 项目名称:health_parent
 * 描述:七牛云对象存储上传图片工具类
 *
 * @author zhong
 * @date 2022-06-16 9:15
 */
public class QiniuUtils {
    
    
    public static String accessKey = "V5sCLYwDcx5YYWvxKVSzeR9imstn7aLZyU22oW0q";
    public static String secretKey = "1-noh3fPNBcT9rVWiZFIhXQ7Atwd_avi4f2VkP9w";
    public static String buckets = "itzcl";

    /**
     * 1、使用七牛云的sdk实现将本地的图片上传到云服务器
     * @param filePath 上传路径
     * @param fileName 指定保存的文件名称
     */
    public static void upload2Qiniu(String filePath,String fileName) {
    
    
        //构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration(Region.region0());
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(buckets);
        try {
    
    
            Response response = uploadManager.put(filePath, fileName, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            System.out.println(putRet.key);
            System.out.println(putRet.hash);
        } catch (QiniuException ex) {
    
    
            Response r = ex.response;
            System.err.println(r.toString());
            try {
    
    
                System.err.println(r.bodyString());
            } catch (QiniuException ex2) {
    
    
                //ignore
            }
        }
    }

    /**
     * 字节数组上传
     * @param bytes 上传的字节
     * @param fileName 指定上传文件的名称
     */
    public static void upload2Qiniu(byte[] bytes, String fileName){
    
    
        //构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration(Region.region0());
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        //默认不指定key的情况下,以文件内容的hash值作为文件名
        String key = fileName;
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(buckets);
        try {
    
    
            Response response = uploadManager.put(bytes, key, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            System.out.println(putRet.key);
            System.out.println(putRet.hash);
        } catch (QiniuException ex) {
    
    
            Response r = ex.response;
            System.err.println(r.toString());
            try {
    
    
                System.err.println(r.bodyString());
            } catch (QiniuException ex2) {
    
    
                //ignore
            }
        }
    }

    /**
     * 删除七牛云的图片
     */
    public static void deleteFileFromQiniu(String fileName){
    
    
        //构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration(Region.region0());

        Auth auth = Auth.create(accessKey, secretKey);
        BucketManager bucketManager = new BucketManager(auth, cfg);
        try {
    
    
            bucketManager.delete(buckets, fileName);
        } catch (QiniuException ex) {
    
    
            //如果遇到异常,说明删除失败
            System.err.println(ex.code());
            System.err.println(ex.response.toString());
        }
    }
}

2、ElementUI图片上传和预览

管理详细使用参考官方文档

2.1、上传页面搭建

<el-form-item label="上传图片">
    <el-upload
            class="avatar-uploader"
            <!--图片上传控制器-->
            action="/setmeal/upload.do"
            <!--是否主动上传-->
            :auto-upload="autoUpload"
   			 <!--获取到上传文件的name-->
            name="imgFile"
             <!--不展示列表-->
            :show-file-list="false"
            <!--上传完成后的提示-->
            :on-success="handleAvatarSuccess"
    		<!--文件上传之前的事件-->
            :before-upload="beforeAvatarUpload">
    	<!--判断显示上传成功后的图片-->
        <img v-if="imageUrl" :src="imageUrl" class="avatar">
        <i v-else class="el-icon-plus avatar-uploader-icon"></i>
    </el-upload>
</el-form-item>

2.2、模型数据

var vue = new Vue({
    
    
            el: '#app',
            data:{
    
    
                autoUpload:true,//自动上传
                imageUrl:null,//模型数据,用于上传图片完成后图片预览
            },methods:{
    
    
            //文件上传成功后的钩子,response为服务端返回的值,file为当前上传的文件封装成的js对象
                handleAvatarSuccess(response, file) {
    
    
                    // 为模型数据imageUrl赋值,用于页面上传图片展示【填写七牛云的外链域名】
                    this.imageUrl = "http://rdjpjlgat.hd-bkt.clouddn.com/"+response.data;
                    this.$message({
    
    
                        type:response.flag ? "success":"errer",
                        message : response.message
                    });
                    // 设置模型数据(图片名称)后续提交ajax请求提交到数据库报错
                    this.formData.img = response.data;
                },
                //上传图片之前执行
                beforeAvatarUpload(file) {
    
    
				  const isJPG = file.type === 'image/jpeg';
				  const isLt2M = file.size / 1024 / 1024 < 2;
				  if (!isJPG) {
    
    
					this.$message.error('上传套餐图片只能是 JPG 格式!');
				  }
				  if (!isLt2M) {
    
    
					this.$message.error('上传套餐图片大小不能超过 2MB!');
				  }
				  return isJPG && isLt2M;
                }
            }
})

2.3、上传图片的控制器

配置springMVC的文件上传

<!--文件上传组件-->
<bean id="multipartResolver"
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="104857600" />
    <property name="maxInMemorySize" value="4096" />
    <property name="defaultEncoding" value="UTF-8"/>
</bean>

编写上传文件的控制器

需要通过MultipartFile来接收上传的文件,并且需要通过@RequestParam("name")注解指定上传文件的name值,否则会获取不到数据

@RestController
@RequestMapping("/setmeal")
public class SetmealController {
    
    

    /**
     * 图片上传
     * @return
     */
    @RequestMapping("/upload")
    public Result upload(@RequestParam("imgFile") MultipartFile imgFile){
    
    
        // 1、获取到上传文件的名称
        String originalFilename = imgFile.getOriginalFilename();
        // 2、获取到最后一个点的位置
        int index = originalFilename.lastIndexOf(".");
        // 3、截取出上传文件的后缀名 .jpg
        String extension = originalFilename.substring(index - 1);
        // 4、通过UUID生产随机不重复的数据拼接新的图片名称
        String fileName = UUID.randomUUID().toString() + extension;
        // 5、调用封装的七牛云工具类上传文件
        try {
    
    
            QiniuUtils.upload2Qiniu(imgFile.getBytes(),fileName);
        } catch (IOException e) {
    
    
            e.printStackTrace();
            return new Result(false, MessageConstant.PIC_UPLOAD_FAIL);
        }
        return new Result(true,MessageConstant.PIC_UPLOAD_SUCCESS,fileName);
    }
}

2.4、完善定时上传区分垃圾图片

前面我们已经完成了文件上传,将图片存储在了七牛云服务器中。但是这个过程存在一个问题,就是如果用户只上传了图片而没有最终保存套餐信息到我们的数据库,这时我们上传的图片就变为了垃圾图片。对于这些垃圾图片我们需要定时清理来释放磁盘空间。这就需要我们能够区分出来哪些是垃圾图片,哪些不是垃圾图片。如何实现呢?

方案就是利用redis来保存图片名称,具体做法为:

1、当用户上传图片后(不一定添加数据),将图片名称保存到redis的一个Set集合中,例如集合名称为setmealPicResources

2、当用户添加套餐后(已经添加到数据库),将图片名称保存到redis的另一个Set集合中,例如集合名称为setmealPicDbResources

3、计算setmealPicResources集合与setmealPicDbResources集合的差值,结果就是垃圾图片的名称集合,清理这些图片即可

使用win版本的redis作为服务器,部署阶段需要使用Linux部署,在win系统下可以使用下面的软件进行直观的看到redis的数据变化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBIIyrmz-1655385753850)(images/image-20220616164553445.png)]

实现步骤

需要在父工程中引入操作redis的依赖坐标

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>${jedis.version}</version>
</dependency>
  1. 在health_backend项目中提供Spring配置文件spring-redis.xml

    如果是部署在虚拟机或云服务上面的需要修改redis链接地址

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    	xmlns:p="http://www.springframework.org/schema/p"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 
    	xmlns:mvc="http://www.springframework.org/schema/mvc"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans 
                             http://www.springframework.org/schema/beans/spring-beans.xsd
            				http://www.springframework.org/schema/mvc 
                             http://www.springframework.org/schema/mvc/spring-mvc.xsd
            				http://code.alibabatech.com/schema/dubbo 
                             http://code.alibabatech.com/schema/dubbo/dubbo.xsd
            				http://www.springframework.org/schema/context 
                             http://www.springframework.org/schema/context/spring-context.xsd">
    
    	<!--Jedis连接池的相关配置-->
    	<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    		<property name="maxTotal">
    			<value>200</value>
    		</property>
    		<property name="maxIdle">
    			<value>50</value>
    		</property>
    		<property name="testOnBorrow" value="true"/>
    		<property name="testOnReturn" value="true"/>
    	</bean>
    	<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
    		<constructor-arg name="poolConfig" ref="jedisPoolConfig" />
    		<constructor-arg name="host" value="127.0.0.1" />
    		<constructor-arg name="port" value="6379" type="int" />
    		<constructor-arg name="timeout" value="30000" type="int" />
    	</bean>
    </beans>
    

    springmvc.xml配置文件中加载redis配置文件

    <!--加载redis配置文件-->
    <import resource="spring-redis.xml"/>
    
  2. 创建一个常量类

    作为redis的key

    package com.itheima.constant;
    
    public class RedisConstant {
          
          
        //套餐图片所有图片名称
        public static final String SETMEAL_PIC_RESOURCES = "setmealPicResources";
        //套餐图片保存在数据库中的图片名称
        public static final String SETMEAL_PIC_DB_RESOURCES = "setmealPicDbResources";
    }
    
  3. 完善图片上传的控制器

    /**
     * 注入jedisPool操作redis
     */
    @Autowired
    private JedisPool jedisPool;
    
    // 在上传图片的控制器中,上传成功后面添加下面代码保存图片到redis
    jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_RESOURCES,fileName);
    
  4. 在保存到数据库的业务工程上添加spring-redis.xml

    复制上面的配置即可

  5. 修改保存到数据库的图片的业务实现类

    /**
     * 注入jedisPool操作redis
     */
    @Autowired
    private JedisPool jedisPool;
    
    // 在添加成功到数据库的下面添加保存到redis
    // 将图片名称保存到redis集合
        String img = setmeal.getImg();
        jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_DB_RESOURCES,img);
    
  6. 启动测试

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UnSx4ROA-1655385753850)(images/image-20220616170914785.png)]

2.5、定时任务组件Qquartz

Quartz介绍

Quartz是Job scheduling(作业调度)领域的一个开源项目,Quartz既可以单独使用也可以跟spring框架整合使用,在实际开发中一般会使用后者。使用Quartz可以开发一个或者多个定时任务,每个定时任务可以单独指定执行的时间,例如每隔1小时执行一次、每个月第一天上午10点执行一次、每个月最后一天下午5点执行一次等。

官网:http://www.quartz-scheduler.org/

maven坐标:

<dependency>
  <groupId>org.quartz-scheduler</groupId>
  <artifactId>quartz</artifactId>
  <version>2.2.1</version>
</dependency>
<dependency>
  <groupId>org.quartz-scheduler</groupId>
  <artifactId>quartz-jobs</artifactId>
  <version>2.2.1</version>
</dependency>

2.5.1、Quartz入门案例

本案例基于Quartz和spring整合的方式使用。具体步骤:

  1. 创建maven工程quartzdemo,导入Quartz和spring相关坐标,pom.xml文件如下

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.zcl</groupId>
        <artifactId>quartz_demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.2.1</version>
            </dependency>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz-jobs</artifactId>
                <version>2.2.1</version>
            </dependency>
        </dependencies>
    
    </project>
    
  2. 自定义一个Job

    表示在指定的调度时间内被执行的方法

    package com.zcl.jobs;
    
    import java.util.Date;
    
    /**
     * 项目名称:health_parent
     * 描述:被执行类
     *
     * @author zhong
     * @date 2022-06-16 19:08
     */
    public class JobDemo {
          
          
        public void run(){
          
          
            System.out.println("job execute..." + new Date());
        }
    }
    
  3. 提供Spring配置文件spring-jobs.xml,配置自定义Job、任务描述、触发器、调度工厂等

    指定触发的时间,基于Cron表达式,下面配置中写的是10秒钟调度一次,如果后面的代码需要变更类和具体的方法修改一下就可以了,同时统一调度配置里面可以调度多个

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
    						http://www.springframework.org/schema/beans/spring-beans.xsd
    						http://www.springframework.org/schema/mvc
    						http://www.springframework.org/schema/mvc/spring-mvc.xsd
    						http://code.alibabatech.com/schema/dubbo
    						http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    						http://www.springframework.org/schema/context
    						http://www.springframework.org/schema/context/spring-context.xsd">
        <!-- 注册自定义Job -->
        <bean id="jobDemo" class="com.zcl.jobs.JobDemo"></bean>
        <!-- 注册JobDetail,作用是负责通过反射调用指定的Job -->
        <bean id="jobDetail"
              class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
            <!-- 注入目标对象 -->
            <property name="targetObject" ref="jobDemo"/>
            <!-- 注入目标方法 -->
            <property name="targetMethod" value="run"/>
        </bean>
        <!-- 注册一个触发器,指定任务触发的时间 -->
        <bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <!-- 注入JobDetail -->
            <property name="jobDetail" ref="jobDetail"/>
            <!-- 指定触发的时间,基于Cron表达式 -->
            <property name="cronExpression">
                <value>0/10 * * * * ?</value>
            </property>
        </bean>
        <!-- 注册一个统一的调度工厂,通过这个调度工厂调度任务 -->
        <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <!-- 注入多个触发器 -->
            <property name="triggers">
                <list>
                    <ref bean="myTrigger"/>
                </list>
            </property>
        </bean>
    </beans>
    
  4. 测试

    如果不手动终止项目就会一直隔10秒调度一次

    public class App {
          
          
        public static void main(String[] args) {
          
          
            new ClassPathXmlApplicationContext("spring-jobs.xml");
        }
    }
    
  5. cron表达式

    上面的入门案例中我们指定了一个表达式:0/10 * * * * ?

    这种表达式称为cron表达式,通过cron表达式可以灵活的定义出符合要求的程序执行的时间。本小节我们就来学习一下cron表达式的使用方法。如下图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lv0gDcFz-1655385753851)(images/image-20220616191949251.png)]

    cron表达式分为七个域,之间使用空格分隔。其中最后一个域(年)可以为空。每个域都有自己允许的值和一些特殊字符构成。使用这些特殊字符可以使我们定义的表达式更加灵活。

    下面是对这些特殊字符的介绍:

    逗号(,):指定一个值列表,例如使用在月域上1,4,5,7表示1月、4月、5月和7月

    横杠(-):指定一个范围,例如在时域上3-6表示3点到6点(即3点、4点、5点、6点)

    星号(*):表示这个域上包含所有合法的值。例如,在月份域上使用星号意味着每个月都会触发

    斜线(/):表示递增,例如使用在秒域上0/15表示每15秒

    问号(?):只能用在日和周域上,但是不能在这两个域上同时使用。表示不指定

    井号(#):只能使用在周域上,用于指定月份中的第几周的哪一天,例如6#3,意思是某月的第三个周五 (6=星期五,3意味着月份中的第三周)

    L:某域上允许的最后一个值。只能使用在日和周域上。当用在日域上,表示的是在月域上指定的月份的最后一天。用于周域上时,表示周的最后一天,就是星期六

    W:W 字符代表着工作日 (星期一到星期五),只能用在日域上,它用来指定离指定日的最近的一个工作日

  6. cron表达式在线生成器

    前面介绍了cron表达式,但是自己编写表达式还是有一些困难的,我们可以借助一些cron表达式在线生成器来根据我们的需求生成表达式即可。

    http://cron.ciding.cc/

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FNGw6ILX-1655385753852)(images/image-20220616193818146.png)]

在项目中maven项目中使用定时任务清理七牛云存储图片

解决方案就是通过定时任务组件定时清理这些垃圾图片。为了能够区分出来哪些图片是垃圾图片,我们在文件上传成功后将图片保存到了一个redis集合中,当套餐数据插入到数据库后我们又将图片名称保存到了另一个redis集合中,通过计算这两个集合的差值就可以获得所有垃圾图片的名称。

本章节我们就会基于Quartz定时任务,通过计算redis两个集合的差值找出所有的垃圾图片,就可以将垃圾图片清理掉。

  1. 创建一个maven的web工程health_jobs,打包方式为war,导入Quartz等相关坐标

    该模块同时引入了一个接口工程

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>health_parent</artifactId>
            <groupId>com.zcl</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
        <packaging>war</packaging>
        <artifactId>health_jobs</artifactId>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
        <dependencies>
            <!--引入接口工程-->
            <dependency>
                <groupId>com.zcl</groupId>
                <artifactId>health_interface</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <!--在父工程指定了版本-->
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
            </dependency>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz-jobs</artifactId>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <configuration>
                        <!-- 指定端口 -->
                        <port>83</port>
                        <!-- 请求路径 -->
                        <path>/</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  2. 配置web.xml

    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
      <!-- 加载spring容器 -->
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext*.xml</param-value>
      </context-param>
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    </web-app>
    
  3. 配置log4j.properties

    ### direct log messages to stdout ###
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target=System.err
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
    
    ### direct messages to file mylog.log ###
    log4j.appender.file=org.apache.log4j.FileAppender
    log4j.appender.file.File=c:\\mylog.log
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
    
    ### set log levels - for more verbose logging change 'info' to 'debug' ###
    
    log4j.rootLogger=info, stdout
    
  4. 配置applicationContext-redis.xml

    需要连接redis数据库获取到里面包存的差值

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--Jedis连接池的相关配置-->
        <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <property name="maxTotal">
                <value>200</value>
            </property>
            <property name="maxIdle">
                <value>50</value>
            </property>
            <property name="testOnBorrow" value="true"/>
            <property name="testOnReturn" value="true"/>
        </bean>
        <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
            <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
            <constructor-arg name="host" value="127.0.0.1" />
            <constructor-arg name="port" value="6379" type="int" />
            <constructor-arg name="timeout" value="30000" type="int" />
        </bean>
    </beans>
    
  5. 配置applicationContext-jobs.xml

    设置间隔时间论调清空云存储图片

    为了方便测试正常,将调度的时间改为了10秒调用一次,项目真正的项目发布时改为注释的时间执行即可

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
    							http://www.springframework.org/schema/beans/spring-beans.xsd
    							http://www.springframework.org/schema/mvc
    							http://www.springframework.org/schema/mvc/spring-mvc.xsd
    							http://code.alibabatech.com/schema/dubbo
    							http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    							http://www.springframework.org/schema/context
    							http://www.springframework.org/schema/context/spring-context.xsd">
        <!--开启注解-->
        <context:annotation-config></context:annotation-config>
        <!--自定义类-->
        <bean id="clearImgJob" class="com.zcl.jobs.ClearImgJob"></bean>
        <bean id="jobDetail"
              class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
            <!-- 注入目标对象 -->
            <property name="targetObject" ref="clearImgJob"/>
            <!-- 注入目标方法 -->
            <property name="targetMethod" value="clearImg"/>
        </bean>
        <!-- 注册一个触发器,指定任务触发的时间 -->
        <bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <!-- 注入JobDetail -->
            <property name="jobDetail" ref="jobDetail"/>
            <!-- 指定触发的时间,基于Cron表达式 -->
            <property name="cronExpression">
                <!--<value>0 0 2 * * ?</value>-->
                <value>0/10 * * * * ?</value>
            </property>
        </bean>
        <!-- 注册一个统一的调度工厂,通过这个调度工厂调度任务 -->
        <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <!-- 注入多个触发器 -->
            <property name="triggers">
                <list>
                    <ref bean="myTrigger"/>
                </list>
            </property>
        </bean>
    </beans>
    
  6. 创建ClearImgJob定时任务类

    package com.zcl.jobs;
    
    import com.itheima.constant.RedisConstant;
    import com.itheima.utils.QiniuUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import redis.clients.jedis.JedisPool;
    
    import java.util.Set;
    
    /**
     * 项目名称:health_parent
     * 描述:自定义job实现定时清理图片
     *
     * @author zhong
     * @date 2022-06-16 20:22
     */
    public class ClearImgJob {
          
          
        /**
         * 注入连接redis数据库
         */
        @Autowired
        private JedisPool jedisPool;
    
        /**
         * 执行清理垃圾图片
         */
        public void clearImg(){
          
          
            // 根据redis中保存的两个set集合进行差值计算,获得垃圾图片的名称
            Set<String> sdiff = jedisPool.getResource().sdiff(RedisConstant.SETMEAL_PIC_RESOURCES, RedisConstant.SETMEAL_PIC_DB_RESOURCES);
            // 判断集合
            if(sdiff != null){
          
          
                // 遍历集合
                for (String picName : sdiff) {
          
          
                    // 调用工具类删除七牛云服务器垃圾图片
                    QiniuUtils.deleteFileFromQiniu(picName);
                    // 删除redis集合中的图片名称【根据名称来删除】
                    jedisPool.getResource().srem(RedisConstant.SETMEAL_PIC_RESOURCES,picName);
                }
            }
        }
    }
    
    
  7. 测试

    七牛云上的图片

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cJSrdL5O-1655385753853)(images/image-20220616203343775.png)]

    redis原来保存的图片数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P0J3U7yx-1655385753853)(images/image-20220616203440855.png)]

    在redis的大集合中存储有一张垃圾图片,下面就要运行程序进行删除了

猜你喜欢

转载自blog.csdn.net/baidu_39378193/article/details/125323594
今日推荐