说明
过期自动清理,程序退出自动清理
使用到了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