|
|
|
@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
package com.zhehekeji.web.service.client;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import com.sourceforge.snap7.moka7.S7Client;
|
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import javax.annotation.PostConstruct;
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
|
|
|
import java.nio.file.FileStore;
|
|
|
|
|
|
|
|
import java.nio.file.Files;
|
|
|
|
|
|
|
|
import java.nio.file.Path;
|
|
|
|
|
|
|
|
import java.nio.file.Paths;
|
|
|
|
|
|
|
|
import java.time.LocalDate;
|
|
|
|
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
|
|
|
|
import java.time.format.DateTimeParseException;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
|
|
import java.util.Comparator;
|
|
|
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import java.util.concurrent.ArrayBlockingQueue;
|
|
|
|
|
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
|
|
|
|
import java.util.concurrent.ThreadPoolExecutor;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
|
|
@Service
|
|
|
|
|
|
|
|
public class DeleteServer {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@PostConstruct
|
|
|
|
|
|
|
|
public void PlcConnectionPool() {
|
|
|
|
|
|
|
|
CompletableFuture.runAsync(() -> cleanStorage("E:\\data\\mp4\\record\\live"));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 清理存储空间
|
|
|
|
|
|
|
|
* @param rootPath 根文件夹路径,例如 "D:/storage"
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private void cleanStorage(String rootPath) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
log.info("开始清理存储空间: {}", rootPath);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Path root = Paths.get(rootPath);
|
|
|
|
|
|
|
|
if (!Files.exists(root) || !Files.isDirectory(root)) {
|
|
|
|
|
|
|
|
log.error("存储路径不存在或不是目录: {}", rootPath);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有相机文件夹下的日期文件夹
|
|
|
|
|
|
|
|
List<DateFolder> allDateFolders = getAllDateFolders(root);
|
|
|
|
|
|
|
|
log.info("共找到 {} 个日期文件夹", allDateFolders.size());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 按日期升序排序(最早的在前面)
|
|
|
|
|
|
|
|
allDateFolders.sort(Comparator.comparing(DateFolder::getDate));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 第一轮清理:删除两个月前的文件夹
|
|
|
|
|
|
|
|
LocalDate twoMonthsAgo = LocalDate.now().minusMonths(2);
|
|
|
|
|
|
|
|
int deletedOld = deleteFoldersBeforeDate(allDateFolders, twoMonthsAgo);
|
|
|
|
|
|
|
|
log.info("删除两个月前的文件夹: {} 个", deletedOld);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查磁盘空间
|
|
|
|
|
|
|
|
if (getDiskUsagePercent(root) < 70.0) {
|
|
|
|
|
|
|
|
log.info("磁盘空间充足({}%),清理完成", getDiskUsagePercent(root));
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 第二轮清理:继续删除最旧的文件夹,直到空间充足或删完
|
|
|
|
|
|
|
|
int deletedMore = deleteOldestFolders(allDateFolders, twoMonthsAgo, root);
|
|
|
|
|
|
|
|
log.info("继续删除最旧的文件夹: {} 个", deletedMore);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info("存储空间清理完成,当前使用率: {}%", getDiskUsagePercent(root));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
log.error("清理存储空间失败", e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 获取所有相机文件夹下的日期文件夹
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private List<DateFolder> getAllDateFolders(Path root) throws IOException {
|
|
|
|
|
|
|
|
List<DateFolder> result = new ArrayList<>();
|
|
|
|
|
|
|
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历根目录下的相机文件夹
|
|
|
|
|
|
|
|
Files.list(root)
|
|
|
|
|
|
|
|
.filter(Files::isDirectory)
|
|
|
|
|
|
|
|
.forEach(cameraDir -> {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
// 遍历相机文件夹下的日期文件夹
|
|
|
|
|
|
|
|
Files.list(cameraDir)
|
|
|
|
|
|
|
|
.filter(Files::isDirectory)
|
|
|
|
|
|
|
|
.forEach(dateDir -> {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
// 尝试解析文件夹名为日期
|
|
|
|
|
|
|
|
LocalDate date = LocalDate.parse(dateDir.getFileName().toString(), formatter);
|
|
|
|
|
|
|
|
result.add(new DateFolder(dateDir.toFile(), date, cameraDir.getFileName().toString()));
|
|
|
|
|
|
|
|
} catch (DateTimeParseException e) {
|
|
|
|
|
|
|
|
// 文件夹名不符合日期格式,跳过
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
log.error("遍历相机文件夹失败: {}", cameraDir, e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 删除指定日期之前的文件夹
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private int deleteFoldersBeforeDate(List<DateFolder> dateFolders, LocalDate beforeDate) {
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
Iterator<DateFolder> iterator = dateFolders.iterator();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (iterator.hasNext()) {
|
|
|
|
|
|
|
|
DateFolder folder = iterator.next();
|
|
|
|
|
|
|
|
if (folder.getDate().isBefore(beforeDate)) {
|
|
|
|
|
|
|
|
if (deleteFolder(folder.getFile())) {
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator.remove();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 继续删除最旧的文件夹,直到空间充足
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private int deleteOldestFolders(List<DateFolder> dateFolders, LocalDate minDate, Path root) {
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
Iterator<DateFolder> iterator = dateFolders.iterator();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (iterator.hasNext() && getDiskUsagePercent(root) >= 70.0) {
|
|
|
|
|
|
|
|
DateFolder folder = iterator.next();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (deleteFolder(folder.getFile())) {
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
log.info("删除文件夹: {}/{} (日期: {}), 当前使用率: {}%",
|
|
|
|
|
|
|
|
folder.getCameraName(), folder.getFile().getName(), folder.getDate(), getDiskUsagePercent(root));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
iterator.remove();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 删除文件夹及其内容
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private boolean deleteFolder(File folder) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
if (folder.exists() && folder.isDirectory()) {
|
|
|
|
|
|
|
|
Files.walk(folder.toPath())
|
|
|
|
|
|
|
|
.sorted(Comparator.reverseOrder())
|
|
|
|
|
|
|
|
.map(Path::toFile)
|
|
|
|
|
|
|
|
.forEach(File::delete);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info("已删除文件夹: {}", folder.getAbsolutePath());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
log.error("删除文件夹失败: {}", folder.getAbsolutePath(), e);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 获取磁盘使用率百分比
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private double getDiskUsagePercent(Path path) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
FileStore store = Files.getFileStore(path);
|
|
|
|
|
|
|
|
long total = store.getTotalSpace();
|
|
|
|
|
|
|
|
long free = store.getUsableSpace();
|
|
|
|
|
|
|
|
long used = total - free;
|
|
|
|
|
|
|
|
return (used * 100.0) / total;
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
log.error("获取磁盘空间失败", e);
|
|
|
|
|
|
|
|
return 100.0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 日期文件夹辅助类
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private static class DateFolder {
|
|
|
|
|
|
|
|
private final File file;
|
|
|
|
|
|
|
|
private final LocalDate date;
|
|
|
|
|
|
|
|
private final String cameraName;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public DateFolder(File file, LocalDate date, String cameraName) {
|
|
|
|
|
|
|
|
this.file = file;
|
|
|
|
|
|
|
|
this.date = date;
|
|
|
|
|
|
|
|
this.cameraName = cameraName;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public File getFile() {
|
|
|
|
|
|
|
|
return file;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public LocalDate getDate() {
|
|
|
|
|
|
|
|
return date;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public String getCameraName() {
|
|
|
|
|
|
|
|
return cameraName;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|