压缩下载

This commit is contained in:
cwchen 2025-04-08 10:30:58 +08:00
parent 6388a84432
commit 9d503f89e3
13 changed files with 167 additions and 70 deletions

View File

@ -35,7 +35,7 @@ public class DownloadController {
@PostMapping("/start")
public ResponseEntity<String> startDownload(@RequestBody DownloadRequest request) {
downloadService.startDownloadTask(request.getTaskId(), request.getProId(),request.getType());
downloadService.startDownloadTask(request.getTaskId(), request.getProId(),request.getType(),request.getProName());
return ResponseEntity.ok("Download task started");
}

View File

@ -114,6 +114,7 @@ public interface SynthesisQueryDao {
/**
* 查询原图/水印照片
*
* @param proId
* @param type
* @return List<String>
@ -121,4 +122,14 @@ public interface SynthesisQueryDao {
* @date 2025/4/7 10:59
*/
List<Photo> findByAlbumId(@Param("proId") String proId, @Param("type") String type);
/**
* 添加下载任务
* @param taskId
* @param nowTime
* @return void
* @author cwchen
* @date 2025/4/8 9:37
*/
void addTaskDownload(@Param("taskId") String taskId, @Param("nowTime") String nowTime,@Param("filePath") String filePath);
}

View File

@ -15,4 +15,5 @@ public class DownloadRequest {
private String taskId;
private String proId;
private String type;
private String proName;
}

View File

@ -12,7 +12,9 @@ import lombok.Data;
@Data
public class Photo {
private String photoId;
private String albumId;
private String filePath;
private String uploadType;
private String uploadTypeName;
private String sourceTypeName;
private String title;
}

View File

@ -3,6 +3,7 @@ package com.bonus.imgTool.backstage.service;
import com.bonus.imgTool.backstage.dao.SynthesisQueryDao;
import com.bonus.imgTool.backstage.entity.Photo;
import com.bonus.imgTool.backstage.entity.ProClassifyStatisticDetailVo;
import com.bonus.imgTool.utils.DateTimeHelper;
import com.bonus.imgTool.utils.SystemUtils;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
@ -52,13 +53,13 @@ public class DownloadService {
private final Map<String, String> taskFileMap = new ConcurrentHashMap<>();
@Async
public void startDownloadTask(String taskId, String proId,String type) {
public void startDownloadTask(String taskId, String proId,String type,String proName) {
Path tempDirPath = null;
try {
// 准备临时目录
tempDirPath = Paths.get(tempDir, taskId);
Files.createDirectories(tempDirPath);
Path zipFilePath = tempDirPath.resolve("photos.zip");
Path zipFilePath = tempDirPath.resolve(proName + ".zip");
// 获取照片列表
List<Photo> photos = getPhotosForAlbum(proId,type);
int total = photos.size();
@ -71,10 +72,12 @@ public class DownloadService {
for (Photo photo : photos) {
String path = SystemUtils.getUploadPath() + File.separator + photo.getFilePath();
Path photoPath = Paths.get(path);
String uniqueEntryName = "photos/" + photo.getPhotoId() + "_" + photoPath.getFileName();
if (!Files.exists(photoPath)) { // 文件不存在
continue;
}
String uniqueEntryName = handlePath(proName,photo,photoPath);
ZipEntry entry = new ZipEntry(uniqueEntryName);
zos.putNextEntry(entry);
try (InputStream is = Files.newInputStream(photoPath)) {
int len;
while ((len = is.read(buffer)) > 0) {
@ -90,17 +93,18 @@ public class DownloadService {
}
}
}
// 移动到输出目录
Path outputDirPath = Paths.get(outputDir);
Files.createDirectories(outputDirPath);
Path finalFilePath = outputDirPath.resolve(taskId + ".zip");
Files.move(zipFilePath, finalFilePath, StandardCopyOption.REPLACE_EXISTING);
// 记录文件位置
taskFileMap.put(taskId, finalFilePath.toString());
// 通知完成
notifyComplete(taskId, "/imgTool/api/download/file?taskId=" + taskId);
// 添加下载任务
String nowTime = DateTimeHelper.currentTwoHours();
synthesisQueryDao.addTaskDownload(taskId,nowTime,finalFilePath.toString());
} catch (Exception e) {
logger.error("下载任务失败: " + taskId, e);
notifyError(taskId, "文件生成失败: " + e.getMessage());
@ -116,6 +120,20 @@ public class DownloadService {
}
}
public String handlePath(String proName,Photo photo,Path photoPath){
StringBuilder sb = new StringBuilder();
sb.append(proName).append(File.separator);
sb.append(photo.getUploadTypeName()).append(File.separator);
if(!Objects.equals(photo.getUploadType(),"5")){
String[] sourceTypeNameArr = photo.getSourceTypeName().split("-");
sb.append(sourceTypeNameArr[1]).append(File.separator);
}else{
sb.append(photo.getTitle()).append(File.separator);
}
sb.append(photo.getPhotoId()).append("_").append(photoPath.getFileName());
return sb.toString();
}
public void addProgressListener(String taskId, DownloadProgressListener listener) {
progressListeners.computeIfAbsent(taskId, k -> new CopyOnWriteArrayList<>()).add(listener);
}

View File

@ -55,7 +55,7 @@ public class BnsSecurityConfig extends WebSecurityConfigurerAdapter {
http.authorizeRequests()
.antMatchers("/", "/*.html", "/favicon.ico", "/css/**", "/js/**", "/fonts/**", "/layui/**", "/img/**"
, "/webjars/**", "/pages/**","/login",
"/statics/**","/websocketServer/**")
"/statics/**","/websocketServer/**","/api/download/**")
.permitAll().anyRequest().authenticated();
http.formLogin().loginProcessingUrl("/login")
.successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler).and()

View File

@ -1,19 +1,25 @@
package com.bonus.imgTool.task;
import com.bonus.imgTool.task.dao.DownloadTaskDao;
import com.bonus.imgTool.task.entity.DownloadTaskVo;
import com.bonus.imgTool.utils.DateTimeHelper;
import com.bonus.imgTool.utils.FileUtil;
import com.bonus.imgTool.utils.SystemUtils;
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.core.SchedulerLock;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.Resource;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* @description 删除原始记录检测报告等文件定时任务
@ -38,69 +44,40 @@ public class DeleteFileTask {
*/
private static final int SCHEDULER_LOCK_MIN = 5 * 60 * 1000;
@Resource(name = "DownloadTaskDao")
private DownloadTaskDao downloadTaskDao;
/**
* 删除原始记录检测报告等文件定时任务
* 使用 cron 表达式指定每天23点执行一次
* 删除文件定时任务
* 使用 cron 表达式指定每十分钟执行一次
*/
@Scheduled(cron = "0 0 23 * * *")
@Scheduled(cron = "0 0/10 * ? * *")
@SchedulerLock(name = "StationMonthTask", lockAtMostFor = SCHEDULER_LOCK_MAX, lockAtLeastFor = SCHEDULER_LOCK_MIN)
@Async
public void getHomeCacheTask() {
log.info("--------删除原始记录、检测报告等文件定时任务开启------");
// 当前日期
String nowDate = DateTimeHelper.getNowDate();
// 删除该日期之前的文件夹
deleteFoldersBeforeDate(nowDate);
log.info("--------删除原始记录、检测报告等文件定时任务执行完毕------");
}
// 删除指定目录下所有在当前日期之前的文件夹
public static void deleteFoldersBeforeDate(String nowDate) {
File dir = new File(SystemUtils.getUploadPath());
if (!dir.exists() || !dir.isDirectory()) {
log.warn("指定的目录不存在或不是一个目录:" + SystemUtils.getUploadPath());
return;
}
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
log.info("--------删除缓存文件定时任务开启------");
try {
// 假设文件夹名符合日期格式yyyy-MM-dd
String folderName = file.getName();
Date folderDate = sdf.parse(folderName);
Date currentDate = sdf.parse(nowDate);
if (folderDate.before(currentDate)) {
deleteDirectory(file);
}
} catch (ParseException e) {
log.error("日期解析失败,文件夹名称可能不符合预期格式:" + file.getName(), e);
} catch (Exception e) {
log.error("处理文件夹时发生异常:" + file.getName(), e);
}
}
}
}
}
// 递归删除文件夹
public static void deleteDirectory(File dir) {
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
deleteDirectory(file);
List<DownloadTaskVo> list = downloadTaskDao.getDownloadTask();
list.forEach(item -> {
if(new File(item.getFilePath()).exists()){
boolean flag = FileUtil.deleteFile2(item.getFilePath());
if(flag){
item.setDelete("0");
}else{
if (!file.delete()) {
log.warn("文件删除失败:" + file.getAbsolutePath());
item.setDelete("1");
}
}else{
item.setDelete("0");
}
});
if(CollectionUtils.isNotEmpty(list)){
downloadTaskDao.updateTaskDownload(list);
}
} catch (Exception e) {
log.error(e.toString(), e);
}
if (!dir.delete()) {
log.warn("目录删除失败:" + dir.getAbsolutePath());
}
// 删除该日期之前的文件夹
log.info("--------删除缓存文件定时任务执行完毕------");
}
}

View File

@ -0,0 +1,28 @@
package com.bonus.imgTool.task.dao;
import com.bonus.imgTool.task.entity.DownloadTaskVo;
import com.bonus.imgTool.task.entity.TaskVo;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository(value = "DownloadTaskDao")
public interface DownloadTaskDao {
/**
* 查询下载定时任务数据
* @return List<DownloadTaskVo>
* @author cwchen
* @date 2025/4/8 9:59
*/
List<DownloadTaskVo> getDownloadTask();
/**
* 更新下载定时任务数据
* @param list
* @return void
* @author cwchen
* @date 2025/4/8 10:01
*/
void updateTaskDownload(List<DownloadTaskVo> list);
}

View File

@ -0,0 +1,26 @@
package com.bonus.imgTool.task.entity;
import lombok.Data;
import java.util.Date;
/**
* @className:DownloadTaskVo
* @author:cwchen
* @date:2025-04-08-9:51
* @version:1.0
* @description:文件下载定时任务
*/
@Data
public class DownloadTaskVo {
private Long id;
/**任务id*/
private String taskId;
/**文件路径*/
private String filePath;
/**失效时间*/
private Date failureTime;
/**是否删除*/
private String delete;
}

View File

@ -845,4 +845,15 @@ public class DateTimeHelper {
}
return strs;
}
public static String currentTwoHours(){
// 获取当前时间
Calendar calendar = Calendar.getInstance();
System.out.println("当前时间: " + calendar.getTime());
// 添加2小时
calendar.add(Calendar.HOUR_OF_DAY, 2);
Date newDate = calendar.getTime();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return dateFormat.format(newDate);
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bonus.imgTool.task.dao.DownloadTaskDao">
<!--更新下载定时任务数据-->
<update id="updateTaskDownload">
<foreach collection="list" item="item" separator=";">
UPDATE tb_download_task
SET is_delete = #{item.delete}
WHERE id = #{item.id}
</foreach>
</update>
<!--查询下载定时任务数据-->
<select id="getDownloadTask" resultType="com.bonus.imgTool.task.entity.DownloadTaskVo">
SELECT id,
task_id AS taskId,
file_path AS filePath
FROM tb_download_task
WHERE is_delete = '1' AND NOW() > failure_time
</select>
</mapper>

View File

@ -131,7 +131,7 @@ body {
.imgData {
width: 100%;
height: 65%;
height: 63%;
}
.img-viewer {
@ -147,7 +147,7 @@ body {
.imgData2 {
width: 100%;
height: 8%;
height: 10%;
justify-content: space-between;
}
.imgData2 > p,.imgData4>p {

View File

@ -29,13 +29,13 @@ document.getElementById('downloadBtn').addEventListener('click', function() {
downloadLink.onclick = function(e) {
e.preventDefault();
window.location.href = data.downloadUrl;
notification.style.display = 'none';
window.close();
// notification.style.display = 'none';
};
notification.style.display = 'block';
// 2小时后自动隐藏通知
setTimeout(() => {
notification.style.display = 'none';
window.close();
}, 1000 * 60 * 60 * 2);
// 关闭EventSource连接
eventSource.close();
@ -62,6 +62,7 @@ document.getElementById('downloadBtn').addEventListener('click', function() {
taskId: taskId,
proId: proId,
type: type,
proName: proName,
})
}).catch(error => {
document.getElementById('statusText').textContent = '启动任务失败';