項目中使用雲服務器上傳圖片產生垃圾文件的處理思路


這里舉例用的是七牛雲,編程語言java,項目的框架是ssm,架構方式是soa

 

首先,什么是垃圾圖片?

1、就是客戶端點擊添加圖片,就會發送請求,將圖片發送到服務端,服務端會生成唯一圖片名稱,然后上傳圖片到七牛雲,並返回給客戶端唯一生成的圖片名稱,但是這個時候注意,並沒有將圖片名稱保存到數據庫中,只是將圖片上傳到了七牛雲。

2、等客戶端將所有表單數據添加完畢后,點擊確認按鈕,會將表單數據一起發送給服務端,表單數據中就包含了圖片的名稱,這個時候才會執行添加到數據庫的操作。

3、而以上兩步,有可能會出現一種情況:就是客戶端在第一步點擊了添加圖片按鈕,就會將圖片上傳到七牛雲並保存,但是數據庫中並沒有保存圖片名,如果這個時候在客戶端點擊了取消按鈕,就不會發生第二步的操作,也就是說,圖片在七牛雲保存着,但是因為沒有保存圖片名到數據庫,而瀏覽七牛雲中的圖片必須要圖片名才能找到,導致這個圖片上傳之后再也找不到了,這種找不到的圖片就是垃圾圖片,光占雲服務器的內存,無法利用。

 

清理垃圾圖片的思路

簡單說,創建兩個容器,執行第一步操作的時候,將圖片名稱存到一個容器中,執行第二步操作的時候,又將圖片名稱存到另一個容器中,如果第一個容器中有的圖片名稱,而第二個容器中沒有對應的圖片名稱(兩個容器中圖片名稱的差集)則可以判定為垃圾圖片,執行七牛雲提供的代碼就可以刪除。

 

 

 

 

具體實現方式舉例:可以使用Redis做這個容器

1、在web工程中添加配置文件:spring_redis.xml

<?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連接池的相關配置 可以參考下面網址
https://www.iteye.com/blog/fengguang0051-2237171
-->
<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" />
<!--Redis的端口號,自己設置,包括上面的ip地址127.0.0.1也可以自己設置-->
<constructor-arg name="port" value="6379" type="int" />
<constructor-arg name="timeout" value="30000" type="int" />
</bean>
</beans>

 

2、但凡配置文件編寫完后,沒有加載等於沒有寫,所以需要加載該配置文件,一般在web工程中會有spring_mvc的配置文件,如果有就在這個配置文件里面引入spring_redis.xml即可,如果沒有就需要到web.xml中加載,這里兩種方式都寫一下

(1)springMVC中引入:

<import resource="classpath:spring-redis.xml"/>

 

(2)在web.xml中加載:二選一即可

<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring_redis.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>

 

3、兩個容器的名字可以選擇寫死,但這里推薦用常量類,利於維護

public class RedisConstant {
//套餐圖片所有圖片名稱
public static final String SETMEAL_PIC_RESOURCES = "setmealPicResources";
//套餐圖片保存在數據庫中的圖片名稱
public static final String SETMEAL_PIC_DB_RESOURCES = "setmealPicDbResources";
}

 

4、准備工作做好了,現在可以開始編寫代碼了(編寫代碼只示范第一個容器了,第二個容器代碼一樣,只是容器名換一下就可以了)

(1)首先在controller中需要注入jedisPool

 

 

 (2)然后:jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_RESOURCES,newFileName);

      這里傳入的兩個參數:第一個是從定義的常量類中取出來的第一個容器名稱,如果沒有定義常量類,直接寫字符串作為容器名稱;

                第二個參數是要保存進容器的圖片名稱

  這一步執行會得到的效果:在redis中創建一個容器,並將上傳成功的圖片名稱保存進這個容器,這樣,客戶端每添加一次圖片,上傳到七牛雲成功后都會在容器中存儲一個圖片名稱

(3)第二個容器也需要創建起來,就是在客戶端點擊確認后,會提交一個表單,表單中包含了圖片名稱,服務端不止要將圖片名稱保存到數據庫,還要保存到另一個容器中,用於跟第一個容器計算差集,這里代碼跟上面一樣,不重復寫了

 

5、兩個容器都有了,那么如何計算插值呢?(這里的舉例只用於測試,需要代碼自動執行並刪除七牛雲中的垃圾圖片的話,需要用到另一個quartz定時任務調度的框架,這個以后說)

可復制代碼如下:

/**
* 測試類(因為是測試類,所以要加下面兩個注解)
*/
@ContextConfiguration(locations = "classpath:spring-redis.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class ClearImgTest {
@Autowired
private JedisPool jedisPool;
// @Test
public void clearImg(){
//傳入第一個容器名稱和第二個容器名稱就可以得到兩個集合的差集
Set<String> sdiff = jedisPool.getResource().sdiff(RedisConstant.SETMEAL_PIC_RESOURCES, RedisConstant.SETMEAL_PIC_DB_RESOURCES);
//先要對差集進行非空判斷,如果沒有垃圾圖片才執行刪除操作
if(sdiff != null && sdiff.size()>0){
//循環差集執行刪除操作,這里的刪除操作是調用七牛雲提供的刪除代碼,我這里將刪除代碼粘貼封裝到自己的util類中了,所以調用QiniuUtils.deleteFileFromQiniu傳入要刪除的文件名就行了
for (String fileName : sdiff) {
//調用七牛雲接口刪除圖片
QiniuUtils.deleteFileFromQiniu(fileName);
}
}
}
}

 

 

這里再來補充這個案例用Quartz框架定時執行刪除垃圾圖片的步驟(想要了解Quartz使用,還可移步看我另外的隨筆Quartz框架使用介紹)

 

1:創建maven聚合工程health_jobs,打包方式為war,導入Quartz等相關坐標

 

  添加相關依賴,這里我的父工程有依賴,如果要直接復制的話會有問題,自己改一下,不會改另外的隨筆Quartz框架使用介紹中有寫,去那里復制

<dependencies>
        <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>84</port>
                    <!-- 請求路徑 -->
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>

 

2:配置web.xml

  • web容器啟動,加載spring容器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
    <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=debug, stdout

 

4:配置applicationContext-redis.xml

  • spring整合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>

 

5:配置applicationContext-jobs.xml

  • spring整合Quartz

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/context
                  http://www.springframework.org/schema/context/spring-context.xsd">

<!--組件的掃描包-->
<context:component-scan base-package="com.baidu"></context:component-scan>

    <!-- 注冊自定義Job -->
    <bean id="jobDemo" class="com.baidu.job.ClearImgJob"></bean>
    <!-- 注冊JobDetail,作用是負責通過反射調用指定的Job -->
    <bean id="jobDetail"
          class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <!-- 注入目標對象 -->
        <property name="targetObject" ref="jobDemo"/>
        <!-- 注入目標方法 -->
        <property name="targetMethod" value="clearImg"/>
    </bean>
    <!-- 注冊一個觸發器,指定任務觸發的時間 -->
    <bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <!-- 注入JobDetail -->
        <property name="jobDetail" ref="jobDetail"/>
        <!-- 指定觸發的時間,基於Cron表達式(0 0 2 * * ?表示凌晨2點執行) -->
        <property name="cronExpression">
            <value>0 0 2 * * ?</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定時任務類(這個上面也有,這里為了完整再來一份)

/**
 * 定時任務:清理垃圾圖片
 */
public class ClearImgJob {

    @Autowired
    private JedisPool jedisPool;
    //清理圖片
    public void clearImg(){
        //計算redis中兩個集合的差值,獲取垃圾圖片名稱
        Set<String> set = jedisPool.getResource().sdiff(
                RedisConstant.SETMEAL_PIC_RESOURCES,
                RedisConstant.SETMEAL_PIC_DB_RESOURCES);
        Iterator<String> iterator = set.iterator();
        while(iterator.hasNext()){
            String pic = iterator.next();
            System.out.println("刪除圖片的名稱是:"+pic);
            //刪除圖片服務器中的圖片文件
            QiniuUtils.deleteFileFromQiniu(pic);
            //刪除redis中的數據
            jedisPool.getResource().srem(RedisConstant.SETMEAL_PIC_RESOURCES,pic);
        }
    }
      //另外,如果想要的話,可以在執行刪除垃圾圖片之后,順手把redis中存的圖片名稱清一下,不然它每次計算插值都是一樣的,一直在重復刪除已經刪除的文件名稱
      //只是執行清空操作的時候,如果同一時間有客戶往redis中保存圖片名稱的話,會存在數據安全問題,比如客戶正常上傳文件,
      //但是在提交表單之前剛好這里執行了清空操作,就會出問題,自己測試后再用,我自己也沒有測過
      jedisPool.getResource().del();//參數要傳需要清空的key,也就是上面說的容器名稱
      jedisPool.getResource().del();


}

 

  • 使用Quartz清理垃圾圖片,直接開啟創建的工程就可以了,工程一開啟就會按照指定的方式定時執行任務類(清理垃圾圖片)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM