文件下载修改

This commit is contained in:
cwchen 2026-02-02 16:59:53 +08:00
parent b1b4077ca5
commit dae8527004
4 changed files with 80 additions and 43 deletions

View File

@ -50,7 +50,11 @@ public class ExportExcelService {
private static final String REDIS_KEY_PREFIX = "export:task:";
// private static final String TEMP_PATH = System.getProperty("java.io.tmpdir") + File.separator + "export_task" + File.separator;
private static final String TEMP_PATH = BonusConfig.getLsFile() + File.separator + "export_task" + File.separator;
// private final String TEMP_PATH = BonusConfig.getLsFile() + File.separator + "export_task" + File.separator;
private String getTempPath() {
return BonusConfig.getLsFile() + File.separator + "export_task" + File.separator;
}
public String createExportTask(ParamsDto dto) {
String taskId = UUID.randomUUID().toString();
@ -68,25 +72,31 @@ public class ExportExcelService {
try {
Date start = dto.getStartTime();
Date end = dto.getEndTime();
// 预先转换好由于比较的 LocalDate
LocalDate startLD = toLocalDate(start);
LocalDate endLD = toLocalDate(end);
long dayDiff = DateUtil.between(start, end, DateUnit.DAY);
File tempDir = new File(TEMP_PATH);
long dayDiff = DateUtil.between(start, end, DateUnit.DAY);
log.info("任务[{}] 临时路径:{}", taskId, getTempPath());
File tempDir = new File(getTempPath());
if (!tempDir.exists()) tempDir.mkdirs();
File finalResultFile;
String downloadFileName;
if (dayDiff <= 0) {
// 单天任务直接处理
// --- 单天任务逻辑保持不变 ---
downloadFileName = "数据识别_" + DateUtil.formatDate(start) + ".xlsx";
finalResultFile = new File(TEMP_PATH + taskId + "_" + downloadFileName);
finalResultFile = new File(getTempPath() + taskId + "_" + downloadFileName);
generateExcel(finalResultFile, start, end, taskId, 0, 100, true);
} else {
// 多天任务多线程处理
// --- 多天任务多线程处理 ---
List<Date> dateRange = getDatesBetween(start, end);
int totalDays = dateRange.size();
// 使用同步列表保证添加安全
List<File> subFiles = Collections.synchronizedList(new ArrayList<>());
CountDownLatch latch = new CountDownLatch(totalDays);
AtomicInteger finishedCount = new AtomicInteger(0);
@ -95,34 +105,40 @@ public class ExportExcelService {
exportThreadPool.execute(() -> {
try {
String subFileName = taskId + "_数据识别_" + DateUtil.formatDate(currentDate) + ".xlsx";
File subFile = new File(TEMP_PATH + subFileName);
// 传入 false表示子线程不直接更新 Redis 整体进度由主线程负责更新
LocalDate currentLD = toLocalDate(end);
// 进行逻辑判断
boolean isSameAsStart = currentLD.equals(startLD);
boolean isSameAsEnd = currentLD.equals(endLD);
if (isSameAsStart && !isSameAsEnd) {
// 情况 1仅与开始日期相同
LocalDateTime endOfDay = currentLD.atTime(LocalTime.MAX);
Date endDate = Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
generateExcel(subFile, start, endDate, taskId, -1, -1, false);
} else if (!isSameAsStart && isSameAsEnd) {
// 情况 2仅与结束日期相同
Date startDate = Date.from(currentLD.atStartOfDay(ZoneId.systemDefault()).toInstant());
generateExcel(subFile, startDate, end, taskId, -1, -1, false);
} else if (!isSameAsStart && !isSameAsEnd) {
// 情况 3都不同
Date startDate = Date.from(currentLD.atStartOfDay(ZoneId.systemDefault()).toInstant());
LocalDateTime endOfDay = currentLD.atTime(LocalTime.MAX);
Date endDate = Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
generateExcel(subFile, startDate, endDate, taskId, -1, -1, false);
File subFile = new File(getTempPath() + subFileName);
// [关键修复] 获取当前循环日期的 LocalDate
LocalDate currentLD = toLocalDate(currentDate);
// [逻辑简化] 计算当前切片的起止时间
Date sliceStartTime;
Date sliceEndTime;
// 1. 确定开始时间如果是第一天用全局start否则用当天的 00:00:00
if (currentLD.equals(startLD)) {
sliceStartTime = start;
} else {
sliceStartTime = Date.from(currentLD.atStartOfDay(ZoneId.systemDefault()).toInstant());
}
// 2. 确定结束时间如果是最后一天用全局end否则用当天的 23:59:59
if (currentLD.equals(endLD)) {
sliceEndTime = end;
} else {
LocalDateTime endOfDay = currentLD.atTime(LocalTime.MAX);
sliceEndTime = Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
}
// 执行生成 (updateProgress=false)
generateExcel(subFile, sliceStartTime, sliceEndTime, taskId, -1, -1, false);
subFiles.add(subFile);
} catch (Exception e) {
log.error("子任务执行失败: {}", currentDate, e);
log.error("子任务[{}] 日期[{}] 执行失败", taskId, DateUtil.formatDate(currentDate), e);
// 可以在这里记录失败的子文件或者抛出异常让主流程感知
} finally {
// 更新整体进度0-95%
int finished = finishedCount.incrementAndGet();
// 进度条平滑处理保留最后几秒给打包
int currentProgress = (int) ((double) finished / totalDays * 95);
updateStatusProgress(taskId, currentProgress, "running");
latch.countDown();
@ -130,21 +146,35 @@ public class ExportExcelService {
});
}
// 最多等待子任务处理 30 分钟
latch.await(30, TimeUnit.MINUTES);
// 等待所有子任务完成
boolean allFinished = latch.await(30, TimeUnit.MINUTES);
if (!allFinished) {
log.warn("任务[{}] 部分子线程超时,正在打包已完成部分", taskId);
}
updateStatusProgress(taskId, 96, "running");
// [优化] 排序子文件多线程执行完顺序是乱的按文件名(日期)排序后再打包解压后体验更好
subFiles.sort(Comparator.comparing(File::getName));
updateStatusProgress(taskId, 98, "running");
String dateRangeStr = DateUtil.format(start, "yyyyMMdd") + "-" + DateUtil.format(end, "yyyyMMdd");
downloadFileName = "批量数据识别_" + dateRangeStr + ".zip";
finalResultFile = new File(TEMP_PATH + taskId + ".zip");
finalResultFile = new File(getTempPath() + taskId + ".zip");
ZipUtil.zip(finalResultFile, false, subFiles.toArray(new File[0]));
subFiles.forEach(File::delete);
// 只有当有文件生成时才打包
if (!subFiles.isEmpty()) {
ZipUtil.zip(finalResultFile, false, subFiles.toArray(new File[0]));
// 删除临时子文件
subFiles.forEach(File::delete);
} else {
throw new RuntimeException("未能生成任何数据文件");
}
}
// 完成任务更新
completeTask(taskId, finalResultFile, downloadFileName);
} catch (Exception e) {
log.error("导出任务异常", e);
handleError(taskId, e);
}
}

View File

@ -42,7 +42,12 @@ public class ExportService {
// 临时文件存放目录
// private static final String TEMP_DIR = System.getProperty("java.io.tmpdir") + "/export_task/";
private static final String TEMP_DIR = BonusConfig.getLsFile() + "/export_task/";
// private final String TEMP_DIR = BonusConfig.getLsFile() + "/export_task/";
// private final String TEMP_DIR = BonusConfig.getLsFile() + "/export_task/";
private String getTempDir() {
return BonusConfig.getLsFile() + File.separator + "export_task" + File.separator;
}
/**
* 创建任务并立即返回 TaskID
@ -121,7 +126,7 @@ public class ExportService {
groupedImages.computeIfAbsent(dateStr, k -> new ArrayList<>()).add(img);
}
File tempDir = new File(TEMP_DIR);
File tempDir = new File(getTempDir());
if (!tempDir.exists()) tempDir.mkdirs();
String masterFileName = "图像识别数据_" + System.currentTimeMillis() + ".zip";
@ -200,12 +205,12 @@ public class ExportService {
*/
private File createDailyZip(String dateStr, List<ImageRecord> images) throws IOException {
// 确保临时目录存在
File tempDir = new File(TEMP_DIR);
File tempDir = new File(getTempDir());
if (!tempDir.exists()) {
tempDir.mkdirs();
}
File dailyZip = new File(TEMP_DIR, "temp_" + dateStr + "_" + UUID.randomUUID() + ".zip");
File dailyZip = new File(getTempDir(), "temp_" + dateStr + "_" + UUID.randomUUID() + ".zip");
try (FileOutputStream fos = new FileOutputStream(dailyZip);
ZipOutputStream dailyZos = new ZipOutputStream(fos)) {

View File

@ -44,7 +44,7 @@ public interface DILineService {
* 删除线的坐标
* @return void
* @author cwchen
* @date 2026/1/20 14:29
* @date 2026/1/20 14:52
*/
void deleteLine();
}

View File

@ -24,15 +24,17 @@ public class FileCleanupTask {
// 使用之前定义的临时目录路径
// private static final String TEMP_PATH = System.getProperty("java.io.tmpdir") + File.separator + "export_task" + File.separator;
private static final String TEMP_PATH = BonusConfig.getLsFile() + File.separator + "export_task" + File.separator;
// private final String TEMP_PATH = BonusConfig.getLsFile() + File.separator + "export_task" + File.separator;
private String getTempPath() {
return BonusConfig.getLsFile() + File.separator + "export_task" + File.separator;
}
/**
* 每天凌晨 2 点执行清理任务
* cron 表达式:
*/
public void cleanupTempFiles() {
log.info("开始执行导出临时文件清理任务...");
File directory = new File(TEMP_PATH);
File directory = new File(getTempPath());
if (!directory.exists() || !directory.isDirectory()) {
log.info("临时目录不存在,跳过清理。");