定时删除的文件工具类

导读:本篇文章讲解 定时删除的文件工具类,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

说明

过期自动清理,程序退出自动清理
使用到了Hutool

工具类

package aaa.bbb.ccc.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.core.util.IdUtil;
import cn.hutool.system.SystemUtil;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 超时自动清理文件,如果程序退出无论是否超时一次清空
 *
 * @author zhongruhang
 */
public class FileTimedCache {
    private final String serialNumber;
    private static FileTimedCache fileTimedCache24Hour;
    private final Map<String, CacheFile> fileMap = new ConcurrentHashMap<>();
    private final long timeOut;
    private static final ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(2, ThreadFactoryBuilder.create().setNamePrefix("缓存文件定时清理-").build());
    private static final List<WeakReference<FileTimedCache>> INSTANCE_LIST = new LinkedList<>();
    private final Map<String, Object> lockMap = new ConcurrentHashMap<>();
    private final ScheduledFuture<?> scheduledFuture;

    static {
        SCHEDULED_THREAD_POOL_EXECUTOR.scheduleWithFixedDelay(() -> INSTANCE_LIST.removeIf(fileTimedCacheWeakReference -> Objects.isNull(fileTimedCacheWeakReference.get())), 1, 1, TimeUnit.MINUTES);
    }

    public FileTimedCache(long timeOut, TimeUnit timeUnit) {
        this.timeOut = timeUnit.toMillis(timeOut);
        scheduledFuture = SCHEDULED_THREAD_POOL_EXECUTOR.scheduleWithFixedDelay(this::prune, timeOut, timeOut, timeUnit);
        Runtime.getRuntime().addShutdownHook(new Thread(FileTimedCache::shutdownAll));
        serialNumber = IdUtil.fastSimpleUUID();
        INSTANCE_LIST.add(new WeakReference<>(this));
    }

    /**
     * 停止所有自动清理线程,立即删除所有临时文件,需要在ServletContext销毁时调用
     */
    public static void shutdownAll() {
        SCHEDULED_THREAD_POOL_EXECUTOR.shutdown();
        Iterator<WeakReference<FileTimedCache>> iterator = INSTANCE_LIST.iterator();
        while (iterator.hasNext()) {
            FileTimedCache fileTimedCache = iterator.next().get();
            if (Objects.isNull(fileTimedCache)) {
                iterator.remove();
                continue;
            }
            for (Map.Entry<String, CacheFile> cacheFileEntry : fileTimedCache.fileMap.entrySet()) {
                try {
                    FileUtil.del(cacheFileEntry.getValue().getFile());
                } catch (IORuntimeException ignored) {
                }
            }
        }
    }

    /**
     * 停止自动清理线程,立即删除临时文件,需要在使用完毕后调用
     */
    public void shutdown() {
        scheduledFuture.cancel(false);
        for (Map.Entry<String, CacheFile> cacheFileEntry : this.fileMap.entrySet()) {
            try {
                FileUtil.del(cacheFileEntry.getValue().getFile());
            } catch (IORuntimeException ignored) {
            }
        }
    }

    private void checkShutdown() {
        Assert.isFalse(SCHEDULED_THREAD_POOL_EXECUTOR.isShutdown(), "Cache shutdown already");
        Assert.isFalse(scheduledFuture.isCancelled(), "Cache shutdown already");
    }

    public static FileTimedCache get24HourInstance() {
        if (Objects.isNull(fileTimedCache24Hour)) {
            fileTimedCache24Hour = new FileTimedCache(24, TimeUnit.HOURS);
        }
        return fileTimedCache24Hour;
    }

    public void put(String key, File file) {
        checkShutdown();
        Objects.requireNonNull(key);
        Objects.requireNonNull(file);
        CacheFile cacheFile = new CacheFile(file, timeOut);
        fileMap.put(key, cacheFile);
        lockMap.put(key, new Object());
    }

    /**
     * @param key      key
     * @param fileName 文件名,提供名字即可无需路径
     */
    public File create(String key, String fileName) {
        checkShutdown();
        Objects.requireNonNull(key);
        Objects.requireNonNull(fileName);
        String tmpFile = SystemUtil.TMPDIR + File.separator + this.getClass().getPackage().getName() + File.separator + serialNumber + File.separator + fileName;
        FileUtil.del(tmpFile);
        File touch = FileUtil.touch(tmpFile);
        put(key, touch);
        return touch;
    }

    public File get(String key) {
        checkShutdown();
        if (Objects.isNull(key)) {
            return null;
        }
        synchronized (lockMap.get(key)) {
            CacheFile cacheFile = fileMap.get(key);
            if (Objects.isNull(cacheFile)) {
                return null;
            }
            cacheFile.updateLastUsedTime();
            return cacheFile.getFile();
        }
    }

    public void remove(String key) {
        checkShutdown();
        if (Objects.isNull(key)) {
            return;
        }
        synchronized (lockMap.get(key)) {
            boolean del = FileUtil.del(get(key));
            Assert.isTrue(del);
            fileMap.remove(key);
            lockMap.remove(key);
        }
    }

    private void prune() {
        System.out.println(Thread.currentThread().getName() + " 清理过期文件");
        for (Map.Entry<String, CacheFile> cacheFileEntry : fileMap.entrySet()) {
            CacheFile cacheFile = cacheFileEntry.getValue();
            if (cacheFile.isExpired()) {
                try {
                    remove(cacheFileEntry.getKey());
                } catch (Exception ignored) {
                }
            }
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        FileTimedCache that = (FileTimedCache) o;
        return Objects.equals(serialNumber, that.serialNumber);
    }

    @Override
    public int hashCode() {
        return Objects.hash(serialNumber);
    }

    static class CacheFile {
        private final File file;
        /**
         * 对象存活时长,0表示永久存活
         */
        private final long ttl;
        /**
         * 上次访问时间
         */
        private volatile long lastAccess;

        /**
         * 构造
         *
         * @param file 值
         * @param ttl  超时时长,毫秒
         */
        public CacheFile(File file, long ttl) {
            this.file = file;
            this.ttl = ttl;
            this.lastAccess = System.currentTimeMillis();
        }

        public boolean isExpired() {
            if (this.ttl > 0) {
                // 此处不考虑时间回拨
                return (System.currentTimeMillis() - this.lastAccess) > this.ttl;
            }
            return false;
        }

        public void updateLastUsedTime() {
            this.lastAccess = System.currentTimeMillis();
        }

        public File getFile() {
            return this.file;
        }
    }
}

使用方法


    void cacheFile() {
        FileTimedCache fileTimedCache = new FileTimedCache(5, DateUnit.SECOND);
        fileTimedCache.put("aa", FileUtil.touch("C:\\Users\\Administrator\\Downloads\\aa"));
        System.out.println("创建aa成功");
        FileTimedCache.shutdownAll();
    }
	void cacheFile1() {
        File bb = FileTimedCache.get24HourInstance().create("bbKey", "bbFileName");
        System.out.println("创建bb成功");
        FileTimedCache.shutdownAll();
    }

    public static void main(String[] args) {
        new BBBApplicationTests().cacheFile();
        new BBBApplicationTests().cacheFile1();
    }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/73783.html

(0)
小半的头像小半

相关推荐

半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!