|
|
@@ -5,11 +5,15 @@ import com.alibaba.fastjson.JSON;
|
|
|
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.http.HttpServletRequest;
|
|
|
import jakarta.servlet.http.HttpServletResponse;
|
|
|
import jakarta.validation.Valid;
|
|
|
-import lombok.AllArgsConstructor;
|
|
|
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;
|
|
|
@@ -17,14 +21,17 @@ import org.sky.scientific.mapper.DownloadTaskMapper;
|
|
|
import org.sky.scientific.pojo.entity.ArchiveAttachEntity;
|
|
|
import org.sky.scientific.pojo.entity.DownloadTask;
|
|
|
import org.sky.scientific.service.IArchiveService;
|
|
|
-import org.slf4j.Logger;
|
|
|
-import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.http.HttpStatus;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
import org.springframework.web.context.request.RequestAttributes;
|
|
|
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.zip.Deflater;
|
|
|
|
|
|
/**
|
|
|
* 科研档案管理 控制器
|
|
|
@@ -32,15 +39,18 @@ import java.io.*;
|
|
|
* @author Kd
|
|
|
* @since 2025-07-01
|
|
|
*/
|
|
|
+@Slf4j
|
|
|
@RestController
|
|
|
-@AllArgsConstructor
|
|
|
@RequestMapping("/archive")
|
|
|
@Tag(name = "科研档案管理")
|
|
|
public class ArchiveCenterController extends KdController {
|
|
|
|
|
|
- private static final Logger log = LoggerFactory.getLogger(ArchiveCenterController.class);
|
|
|
- private final IArchiveService service;
|
|
|
- private final DownloadTaskMapper downloadTaskMapper;
|
|
|
+ @Resource
|
|
|
+ private IArchiveService service;
|
|
|
+ @Resource
|
|
|
+ private DownloadTaskMapper downloadTaskMapper;
|
|
|
+ @Value("${archive.dir}")
|
|
|
+ private String archiveDir;
|
|
|
|
|
|
@GetMapping("fjbczl")
|
|
|
@ApiOperationSupport(order = 1)
|
|
|
@@ -82,7 +92,7 @@ public class ArchiveCenterController extends KdController {
|
|
|
} else {
|
|
|
task.setYearAndMonth(task.getYearAndMonth());
|
|
|
task.setTenantId(SecureUtil.getTenantId());
|
|
|
- int result = downloadTaskMapper.insertDownloadTask( task);
|
|
|
+ int result = downloadTaskMapper.insertDownloadTask(task);
|
|
|
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
|
|
if (result == 1) {
|
|
|
new Thread(() -> {
|
|
|
@@ -107,33 +117,126 @@ public class ArchiveCenterController extends KdController {
|
|
|
writer.write(JSON.toJSONString(R.fail("下载任务不存在,请联系管理员")));
|
|
|
writer.flush();
|
|
|
} else if (task.getStatus() == 2) {
|
|
|
- // 指定磁盘上已存在的ZIP文件路径
|
|
|
- File zipFile = new File(task.getZipUrl());
|
|
|
+ String fileName = null;
|
|
|
+ if (task.getType() == 0) {
|
|
|
+ fileName = "全部档案资料";
|
|
|
+ } else if (task.getType() == 1) {
|
|
|
+ fileName = "高新备查资料";
|
|
|
+ } else {
|
|
|
+ fileName = "加计扣除备查资料";
|
|
|
+ }
|
|
|
+ Path folderPath = Paths.get(archiveDir, SecureUtil.getTenantId(), task.getYearAndMonth(), fileName);
|
|
|
|
|
|
- // 检查文件是否存在
|
|
|
- if (!zipFile.exists()) {
|
|
|
- response.sendError(HttpStatus.NOT_FOUND.value(), "ZIP文件未找到");
|
|
|
+ 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);
|
|
|
|
|
|
- // 设置响应头
|
|
|
- response.setContentType("application/zip");
|
|
|
- response.setHeader("Content-Disposition",
|
|
|
- "attachment; filename=\"" + zipFile.getName() + "\"");
|
|
|
- response.setContentLengthLong(zipFile.length());
|
|
|
+ compressFolderWithApache(folder, folder.getName(), zos);
|
|
|
|
|
|
- // 将文件写入响应输出流
|
|
|
- try (InputStream fileInputStream = new FileInputStream(zipFile);
|
|
|
- OutputStream responseOutputStream = response.getOutputStream()) {
|
|
|
+ zos.finish();
|
|
|
+ response.flushBuffer();
|
|
|
|
|
|
- byte[] buffer = new byte[4096];
|
|
|
- int bytesRead;
|
|
|
- while ((bytesRead = fileInputStream.read(buffer)) != -1) {
|
|
|
- responseOutputStream.write(buffer, 0, bytesRead);
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("压缩文件夹失败: {}", folderPath, e);
|
|
|
+ if (!response.isCommitted()) {
|
|
|
+ sendErrorResponse(response, "文件压缩失败");
|
|
|
}
|
|
|
- responseOutputStream.flush();
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用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()) {
|
|
|
+
|
|
|
+ // 创建目录条目
|
|
|
+ ZipArchiveEntry dirEntry = new ZipArchiveEntry(entryName + "/");
|
|
|
+ zos.putArchiveEntry(dirEntry);
|
|
|
+ zos.closeArchiveEntry();
|
|
|
+
|
|
|
+ // 递归处理子目录
|
|
|
+ compressFolderWithApache(file, entryName, zos);
|
|
|
+ } else {
|
|
|
+ // 创建文件条目
|
|
|
+ ZipArchiveEntry fileEntry = new ZipArchiveEntry(file, entryName);
|
|
|
+ zos.putArchiveEntry(fileEntry);
|
|
|
+
|
|
|
+ try (FileInputStream fis = new FileInputStream(file)) {
|
|
|
+ IOUtils.copy(fis, zos);
|
|
|
+ }
|
|
|
+ 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());
|
|
|
+
|
|
|
+ try (PrintWriter writer = response.getWriter()) {
|
|
|
+ writer.write(JSON.toJSONString(R.fail(message)));
|
|
|
+ writer.flush();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置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");
|
|
|
+ } 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();
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|