Эх сурвалжийг харах

案卷查询下载分片下载

cr 3 долоо хоног өмнө
parent
commit
4275213dfa

+ 113 - 64
blade-service/blade-archive/src/main/java/org/springblade/archive/service/impl/ArchivesAutoServiceImpl.java

@@ -91,6 +91,7 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.*;
 import java.math.BigDecimal;
 import java.net.URL;
+import java.net.URLEncoder;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -102,6 +103,9 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 /**
  *  服务实现类
@@ -3701,82 +3705,127 @@ public class ArchivesAutoServiceImpl extends BaseServiceImpl<ArchivesAutoMapper,
 	@Override
 	public void batchDownloadFileToZip(String ids, HttpServletResponse response) {
 		List<Long> longs = Func.toLongList(ids);
-		//获取档案下的文件
+		// 获取档案下的文件
 		List<ArchiveFile> result = baseMapper.batchSearchArchiveFile(longs);
-		//判断是否存在文件
-		if (result != null && result.size() > 0) {
-			//获取选择的案卷,只要id和文件提名字段
-			Map<Long, String> nameMap = baseMapper.getArchives(longs).stream().filter(l -> StringUtils.isNotBlank(l.getName())).collect(Collectors.toMap(l -> l.getId(), l -> l.getName()));
-			//默认下载地址
-			String defaultDir = "/www/wwwroot/Users/hongchuangyanfa/Desktop/archiveDownload";
-			//项目下地址
-			String projectDir = defaultDir + "/" + result.get(0).getProjectId();
-			File file = new File(projectDir);
-			//获取项目名称
-			ProjectInfo projectInfo = projectClient.getById(result.get(0).getProjectId());
-			//判断文件夹是否存在,存在则该项目正在下载打包,不存在则生成
-			if (file.exists()) {
-				throw new ServiceException("当前项目正在下载档案,请稍后再试");
-			} else {
-				file.mkdir();
+
+		if (CollectionUtils.isEmpty(result)) {
+			throw new ServiceException("未找到可下载的文件");
+		}
+
+		// 获取选择的案卷名称映射
+		Map<Long, String> nameMap = baseMapper.getArchives(longs).stream()
+				.filter(l -> StringUtils.isNotBlank(l.getName()))
+				.collect(Collectors.toMap(l -> l.getId(), l -> l.getName()));
+
+		// 使用临时目录而不是固定目录,避免并发问题
+		String tempDir = System.getProperty("java.io.tmpdir") + "/archive_download_" + UUID.randomUUID();
+		String projectDir = tempDir + "/" + result.get(0).getProjectId();
+		File projectDirFile = new File(projectDir);
+
+		try {
+			// 创建项目目录
+			if (!projectDirFile.mkdirs()) {
+				throw new ServiceException("无法创建临时目录");
 			}
-			//删除掉pdfUrl为空的数据
+
+			// 获取项目信息
+			ProjectInfo projectInfo = projectClient.getById(result.get(0).getProjectId());
+			String zipFileName = projectInfo.getId() + ".zip";
+
+			// 过滤掉无PDF的文件
 			result.removeIf(query -> StringUtils.isEmpty(query.getPdfFileUrl()));
-			//按照档案id分组
-			Map<Long, List<ArchiveFile>> map = result.stream().collect(Collectors.groupingBy(ArchiveFile::getArchiveId));
-			try {
-				//为每个档案分别设置
-				for (Long archiveId : map.keySet()) {
-					String initName = nameMap.get(archiveId);
-					String[] split = initName.split(")");
-					String name = split[split.length - 1];
-					String archiveDir = projectDir + "/" + name;
-					//创建档案文件夹
-					File file2 = new File(archiveDir);
-					file2.mkdir();
-					//组卷,下载PDF
+
+			// 按照档案id分组
+			Map<Long, List<ArchiveFile>> archiveFilesMap = result.stream()
+					.collect(Collectors.groupingBy(ArchiveFile::getArchiveId));
+
+			// 设置响应头 - 提前设置以便流式传输
+			response.setContentType("application/zip");
+			response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipFileName, "UTF-8"));
+
+			// 使用ZipOutputStream直接流式写入响应,避免生成临时zip文件
+			try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
+				// 设置压缩级别
+				zos.setLevel(Deflater.BEST_SPEED);
+
+				// 为每个档案处理文件
+				for (Map.Entry<Long, List<ArchiveFile>> entry : archiveFilesMap.entrySet()) {
+					Long archiveId = entry.getKey();
+					List<ArchiveFile> files = entry.getValue();
+					String archiveName = getArchiveName(nameMap.get(archiveId));
+
+					// 处理合并的档案PDF
 					String mergeArchivesFile = this.getMergeArchivesFile(archiveId);
-					InputStream file_out2 = CommonUtil.getOSSInputStream(mergeArchivesFile);
-					if (file_out2 != null) {
-						CommonUtil.inputStreamToFile(file_out2, new File(archiveDir + "/" + name + ".pdf"));
-						file_out2.close();
+					if (StringUtils.isNotBlank(mergeArchivesFile)) {
+						addFileToZip(zos, archiveName + "/" + archiveName + ".pdf", mergeArchivesFile);
 					}
 
-					//下载卷内文件
-					String archiveFileDir = archiveDir + "/" + "卷内文件";
-					File file3 = new File(archiveFileDir);
-					file3.mkdir();
-					List<ArchiveFile> files = map.get(archiveId);
+					// 处理卷内文件
+					String innerDir = archiveName + "/卷内文件/";
 					for (ArchiveFile archiveFile : files) {
-						String fileUrl = archiveFile.getPdfFileUrl();
-						String initFileName = archiveFile.getFileName();
-						if (initFileName.length() > 100) {
-							initFileName = initFileName.substring(0, 100);
-						}
-						String fileName = "/" + initFileName + ".pdf";
-						InputStream file_out = CommonUtil.getOSSInputStream(fileUrl);
-						if (file_out != null) {
-							CommonUtil.inputStreamToFile(file_out, new File(archiveFileDir + fileName));
-							file_out.close();
-						}
+						String fileName = getSafeFileName(archiveFile.getFileName()) + ".pdf";
+						addFileToZip(zos, innerDir + fileName, archiveFile.getPdfFileUrl());
 					}
 				}
-				//下载完成,打包文件
-				this.packageZip2(defaultDir, projectDir, projectInfo.getId());
-				String zipFile = defaultDir + "/" + projectInfo.getId() + ".zip";
-				Path path = Paths.get(zipFile);
-				response.setContentType("application/zip");
-				response.setHeader("Content-Disposition", "attachment;filename=" + projectInfo.getId() + "2.zip");
-				// 获取文件内容流并写入响应
-				Files.copy(path, response.getOutputStream());
+			}
+		} catch (Exception e) {
+			throw new ServiceException("文件下载失败: " + e.getMessage());
+		} finally {
+			// 清理临时目录
+			deleteDirectory(new File(tempDir));
+		}
+	}
 
-			} catch (Exception e) {
-				throw new ServiceException(e.getMessage());
-			} finally {
-				//删除文件与zip文件
-				this.deleteFile(defaultDir, projectInfo.getId());
+	// 辅助方法:添加文件到zip流(分片下载)
+	private void addFileToZip(ZipOutputStream zos, String entryName, String fileUrl) throws IOException {
+		if (StringUtils.isBlank(fileUrl)) return;
+
+		ZipEntry zipEntry = new ZipEntry(entryName);
+		zos.putNextEntry(zipEntry);
+
+		try (InputStream in = CommonUtil.getOSSInputStream(fileUrl)) {
+			if (in == null) return;
+
+			byte[] buffer = new byte[8192]; // 8KB缓冲区
+			int len;
+			while ((len = in.read(buffer)) > 0) {
+				zos.write(buffer, 0, len);
+			}
+		} finally {
+			zos.closeEntry();
+		}
+	}
+
+	// 辅助方法:安全获取档案名称
+	private String getArchiveName(String initName) {
+		if (StringUtils.isBlank(initName)) return "未命名档案";
+		String[] split = initName.split(")");
+		return split[split.length - 1];
+	}
+
+	// 辅助方法:安全文件名
+	private String getSafeFileName(String fileName) {
+		if (StringUtils.isBlank(fileName)) return "未命名文件";
+		// 限制文件名长度并移除非法字符
+		return fileName.substring(0, Math.min(100, fileName.length()))
+				.replaceAll("[\\\\/:*?\"<>|]", "");
+	}
+
+	// 辅助方法:递归删除目录
+	private void deleteDirectory(File directory) {
+		if (directory == null || !directory.exists()) return;
+
+		File[] files = directory.listFiles();
+		if (files != null) {
+			for (File file : files) {
+				if (file.isDirectory()) {
+					deleteDirectory(file);
+				} else {
+					file.delete();
+				}
 			}
 		}
+		directory.delete();
 	}
 
 	@Override