|
|
@@ -6,17 +6,16 @@ import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
import jakarta.annotation.Resource;
|
|
|
+import jakarta.servlet.ServletOutputStream;
|
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
|
import jakarta.servlet.http.HttpServletResponse;
|
|
|
import jakarta.validation.Valid;
|
|
|
import lombok.SneakyThrows;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
|
|
-import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
|
|
-import org.apache.commons.compress.utils.IOUtils;
|
|
|
import org.sky.core.boot.ctrl.KdController;
|
|
|
import org.sky.core.secure.utils.SecureUtil;
|
|
|
import org.sky.core.tool.api.R;
|
|
|
+import org.sky.core.tool.utils.DateUtil;
|
|
|
import org.sky.scientific.mapper.DownloadTaskMapper;
|
|
|
import org.sky.scientific.pojo.entity.ArchiveAttachEntity;
|
|
|
import org.sky.scientific.pojo.entity.DownloadTask;
|
|
|
@@ -29,10 +28,7 @@ import org.springframework.web.context.request.RequestContextHolder;
|
|
|
|
|
|
import java.io.*;
|
|
|
import java.net.URLEncoder;
|
|
|
-import java.nio.file.Path;
|
|
|
-import java.nio.file.Paths;
|
|
|
-import java.util.Date;
|
|
|
-import java.util.zip.Deflater;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
|
|
|
/**
|
|
|
* 科研档案管理 控制器
|
|
|
@@ -46,6 +42,8 @@ import java.util.zip.Deflater;
|
|
|
@Tag(name = "科研档案管理")
|
|
|
public class ArchiveCenterController extends KdController {
|
|
|
|
|
|
+ private static final int BUFFER_SIZE = 8192; // 8KB缓冲区
|
|
|
+
|
|
|
@Resource
|
|
|
private IArchiveService service;
|
|
|
@Resource
|
|
|
@@ -78,12 +76,12 @@ public class ArchiveCenterController extends KdController {
|
|
|
@ApiOperationSupport(order = 6)
|
|
|
@Operation(summary = "档案中心-一键下载/一键导出高新备查/一键导出加计备查-生成", description = "返回任务Id,后端生产待下载的文件")
|
|
|
public R generateCenterZipFiles(@Valid @RequestBody DownloadTask task) {
|
|
|
- if (task.getId() != null) {
|
|
|
+ if (task.getId() != null) {
|
|
|
DownloadTask dbTask = downloadTaskMapper.selectById(task.getId());
|
|
|
if (dbTask == null) {
|
|
|
return R.fail("下载任务不存在,请联系管理员");
|
|
|
}
|
|
|
- if (dbTask.getExpireTime().compareTo(new Date()) <= 0) {
|
|
|
+ if (dbTask.getStatus() == 2 && dbTask.getExpireTime() != null && DateUtil.now().compareTo(dbTask.getExpireTime()) > 0) {
|
|
|
// 任务已过期
|
|
|
dbTask.setStatus(3);
|
|
|
}
|
|
|
@@ -104,22 +102,6 @@ public class ArchiveCenterController extends KdController {
|
|
|
return R.success();
|
|
|
}
|
|
|
|
|
|
-// @Scheduled(cron = "0 0/5 * * * ?")
|
|
|
-// public void dealWithDownloadTask() {
|
|
|
-// //处理下载任务,主要针对未完成的任务
|
|
|
-// LambdaQueryWrapper<DownloadTask> wrapper = Wrappers.<DownloadTask>lambdaQuery()
|
|
|
-// .eq(DownloadTask::getStatus, 0)
|
|
|
-// .le(DownloadTask::getCreateTime, DateUtil.minusMinutes(new Date(), 5));
|
|
|
-// List<DownloadTask> list = downloadTaskMapper.selectList(wrapper);
|
|
|
-// RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
|
|
-// for (DownloadTask task : list) {
|
|
|
-// new Thread(() -> {
|
|
|
-// RequestContextHolder.setRequestAttributes(requestAttributes);
|
|
|
-// service.downloadCenterZipFiles(task);
|
|
|
-// }).start();
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
@SneakyThrows
|
|
|
@GetMapping("center/download")
|
|
|
@ApiOperationSupport(order = 6)
|
|
|
@@ -132,128 +114,81 @@ public class ArchiveCenterController extends KdController {
|
|
|
writer.write(JSON.toJSONString(R.fail("下载任务不存在,请联系管理员")));
|
|
|
writer.flush();
|
|
|
} else if (task.getStatus() == 2) {
|
|
|
- String fileName = null;
|
|
|
- if (task.getType() == 0) {
|
|
|
- fileName = "全部档案资料";
|
|
|
- } else if (task.getType() == 1) {
|
|
|
- fileName = "高新备查资料";
|
|
|
- } else {
|
|
|
- fileName = "加计扣除备查资料";
|
|
|
+ File file = new File(task.getZipUrl());
|
|
|
+ if (!file.exists()) {
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ PrintWriter writer = response.getWriter();
|
|
|
+ writer.write(JSON.toJSONString(R.fail("文件不存在")));
|
|
|
+ writer.flush();
|
|
|
}
|
|
|
- Path folderPath = Paths.get(archiveDir, SecureUtil.getTenantId(), task.getYearAndMonth(), fileName);
|
|
|
|
|
|
- File folder = folderPath.toFile();
|
|
|
- if (!folder.exists() || !folder.isDirectory()) {
|
|
|
- sendErrorResponse(response, "文件夹不存在或不是目录");
|
|
|
- return;
|
|
|
- }
|
|
|
- setupZipResponseHeaders(response, task.getYearAndMonth() + "年" + fileName + ".zip");
|
|
|
|
|
|
- try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(response.getOutputStream())) {
|
|
|
- zos.setEncoding("UTF-8"); // 支持中文文件名
|
|
|
- zos.setLevel(Deflater.BEST_SPEED);
|
|
|
+ try (FileInputStream fis = new FileInputStream(file);
|
|
|
+ BufferedInputStream bis = new BufferedInputStream(fis);
|
|
|
+ ServletOutputStream outputStream = response.getOutputStream()) {
|
|
|
|
|
|
- Date date = new Date();
|
|
|
- compressFolderWithApache(folder, folder.getName(), zos);
|
|
|
- log.info("压缩耗时:{}秒", (new Date().getTime() - date.getTime()) / 1000);
|
|
|
+ // 设置内容类型
|
|
|
+ response.setContentType("application/octet-stream");
|
|
|
|
|
|
- zos.finish();
|
|
|
- response.flushBuffer();
|
|
|
+ // 设置文件大小
|
|
|
+ response.setContentLengthLong(file.length());
|
|
|
|
|
|
- } catch (IOException e) {
|
|
|
- log.error("压缩文件夹失败: {}", folderPath, e);
|
|
|
- if (!response.isCommitted()) {
|
|
|
- sendErrorResponse(response, "文件压缩失败");
|
|
|
- }
|
|
|
- }
|
|
|
+ // 设置下载文件名(解决中文乱码)
|
|
|
+ String encodedFileName = encodeFileName(file.getName());
|
|
|
+ response.setHeader("Content-Disposition",
|
|
|
+ "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
|
|
|
|
|
|
- }
|
|
|
- }
|
|
|
+ // 缓存控制
|
|
|
+ response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
|
+ response.setHeader("Pragma", "no-cache");
|
|
|
+ response.setHeader("Expires", "0");
|
|
|
|
|
|
- /**
|
|
|
- * 使用Apache Commons Compress压缩文件夹
|
|
|
- */
|
|
|
- private void compressFolderWithApache(File folder, String basePath,
|
|
|
- ZipArchiveOutputStream zos) throws IOException {
|
|
|
- File[] files = folder.listFiles();
|
|
|
- if (files == null) return;
|
|
|
-
|
|
|
- for (File file : files) {
|
|
|
- String entryName = basePath + "/" + file.getName();
|
|
|
-
|
|
|
- if (file.isDirectory()) {
|
|
|
+ // 安全头
|
|
|
+ response.setHeader("X-Content-Type-Options", "nosniff");
|
|
|
|
|
|
- // 创建目录条目
|
|
|
- ZipArchiveEntry dirEntry = new ZipArchiveEntry(entryName + "/");
|
|
|
- zos.putArchiveEntry(dirEntry);
|
|
|
- zos.closeArchiveEntry();
|
|
|
+ // 传输文件数据
|
|
|
+ byte[] buffer = new byte[8192];
|
|
|
+ int bytesRead;
|
|
|
+ long totalBytes = 0;
|
|
|
|
|
|
- // 递归处理子目录
|
|
|
- compressFolderWithApache(file, entryName, zos);
|
|
|
- } else {
|
|
|
- // 创建文件条目
|
|
|
- ZipArchiveEntry fileEntry = new ZipArchiveEntry(file, entryName);
|
|
|
- zos.putArchiveEntry(fileEntry);
|
|
|
-
|
|
|
- try (FileInputStream fis = new FileInputStream(file)) {
|
|
|
- IOUtils.copy(fis, zos);
|
|
|
+ while ((bytesRead = bis.read(buffer)) != -1) {
|
|
|
+ outputStream.write(buffer, 0, bytesRead);
|
|
|
+ totalBytes += bytesRead;
|
|
|
}
|
|
|
- zos.closeArchiveEntry();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- /**
|
|
|
- * 发送错误JSON响应
|
|
|
- */
|
|
|
- private void sendErrorResponse(HttpServletResponse response, String message) throws IOException {
|
|
|
- response.setContentType("application/json;charset=UTF-8");
|
|
|
- response.setCharacterEncoding("UTF-8");
|
|
|
- response.setStatus(HttpStatus.BAD_REQUEST.value());
|
|
|
+ outputStream.flush();
|
|
|
+ log.info("文件下载完成: {}, 大小: {} mb", task.getZipUrl(), totalBytes/1024/1024);
|
|
|
|
|
|
- try (PrintWriter writer = response.getWriter()) {
|
|
|
- writer.write(JSON.toJSONString(R.fail(message)));
|
|
|
- writer.flush();
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("文件下载失败: {}", task.getZipUrl(), e);
|
|
|
+ setErrorResponse(response, "文件下载失败", HttpStatus.INTERNAL_SERVER_ERROR);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
- * 设置ZIP下载响应头
|
|
|
- */
|
|
|
- private void setupZipResponseHeaders(HttpServletResponse response, String fileName) {
|
|
|
- response.setContentType("application/zip");
|
|
|
- response.setCharacterEncoding("UTF-8");
|
|
|
- response.setHeader("Content-Disposition",
|
|
|
- "attachment; filename=\"" + encodeFileName(fileName) + "\"");
|
|
|
- response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * 文件名编码处理(防止中文乱码)
|
|
|
+ * 编码文件名
|
|
|
*/
|
|
|
private String encodeFileName(String fileName) {
|
|
|
try {
|
|
|
- return URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
|
|
|
+ return URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString())
|
|
|
+ .replaceAll("\\+", "%20");
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
|
return fileName;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 文件传输
|
|
|
+ * 设置错误响应
|
|
|
*/
|
|
|
- private void transferFileToResponse(File file, HttpServletResponse response) throws IOException {
|
|
|
- try (FileInputStream fileInputStream = new FileInputStream(file);
|
|
|
- OutputStream responseOutputStream = response.getOutputStream()) {
|
|
|
-
|
|
|
- byte[] buffer = new byte[4096];
|
|
|
- int bytesRead;
|
|
|
- while ((bytesRead = fileInputStream.read(buffer)) != -1) {
|
|
|
- responseOutputStream.write(buffer, 0, bytesRead);
|
|
|
- }
|
|
|
- responseOutputStream.flush();
|
|
|
+ private void setErrorResponse(HttpServletResponse response, String message, HttpStatus status) {
|
|
|
+ try {
|
|
|
+ response.setStatus(status.value());
|
|
|
+ response.setContentType("application/json");
|
|
|
+ response.getWriter().write("{\"error\": \"" + message + "\"}");
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("设置错误响应失败", e);
|
|
|
}
|
|
|
}
|
|
|
}
|