Browse Source

[task-3907]新增打包下载接口

szjbdgzl 1 year ago
parent
commit
86bc8c039e

+ 57 - 59
ibps-comp-root/modules/comp-file-server/src/main/java/com/lc/ibps/cloud/file/provider/DownloadProvider.java

@@ -2,25 +2,25 @@ package com.lc.ibps.cloud.file.provider;
 
 import java.io.*;
 import java.net.URL;
-import java.util.HashMap;
-import java.util.List;
+import java.net.URLEncoder;
 import java.util.Map;
 import java.util.Scanner;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import ch.qos.logback.core.joran.util.StringToObjectConverter;
-import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
-import com.google.gson.JsonObject;
 import com.lc.ibps.base.core.util.*;
 import com.lc.ibps.base.framework.id.UniqueIdUtil;
 import com.lc.ibps.cloud.redis.utils.RedisUtil;
 import com.lc.ibps.common.file.persistence.entity.AttachmentPo;
-import org.apache.commons.io.FileUtils;
 import org.jodconverter.OfficeDocumentConverter;
 import org.jodconverter.office.DefaultOfficeManagerBuilder;
 import org.jodconverter.office.OfficeException;
@@ -88,76 +88,74 @@ public class DownloadProvider extends GenericUploadProvider implements IDownload
 	public void downloadZip(
 			@ApiParam(name = "attachmentIds", value = "附件ID数组", required = true)
 			@RequestParam(name = "attachmentIds", required = true) String[] attachmentIds) {
-		String realFilePath = null;
-		String zipFilePath = null;
-		Map<String, Integer> fileNameCounter = new HashMap<>();  // 用于处理重复文件名
+		// 初始化计数器用于重复文件名处理
+		Map<String, AtomicInteger> fileNameCounter = new ConcurrentHashMap<>();
 		try {
-			// 创建临时目录
-			String rootRealPath = AppFileUtil.getRealPath("/"+AppFileUtil.TEMP_PATH);
-			String uuid = UniqueIdUtil.getId();
-			String folderName = "downloads_" + uuid;
-			realFilePath = rootRealPath + File.separator + folderName;
-			File targetDir = new File(realFilePath);
-			if (!targetDir.exists()) {
-				targetDir.mkdirs();
-			}
-			// 循环处理每个文件
-			this.getUploadService();
-			for (String attachmentId : attachmentIds) {
-				try {
-					FileInfo fileInfo = uploadService.downloadFile(attachmentId.trim());
-					if (fileInfo == null || fileInfo.getFileBytes() == null) {
-						logger.warn("文件不存在: {}", attachmentId);
-						continue;
-					}
-					// 生成唯一文件名
-					String originalName = UploadUtil.getFileName(fileInfo.getFileName(),fileInfo.getExt());
-					String safeName = generateUniqueName(originalName, fileNameCounter);
-					// 写入临时文件
-					File outputFile = new File(targetDir, safeName);
-					FileUtils.writeByteArrayToFile(outputFile, fileInfo.getFileBytes());
-				} catch (Exception e) {
-					logger.error("文件处理失败: {}", attachmentId, e);
+			// 设置响应头(优化点1:使用RFC 5987标准编码)
+			HttpServletResponse response = this.getResponse();
+			response.setContentType("application/zip");
+			String folderName = "downloads_" + UniqueIdUtil.getId() + ".zip";
+			String encodedFileName = URLEncoder.encode(folderName, "UTF-8").replaceAll("\\+", "%20"); // 空格特殊处理
+			response.setHeader("Content-Disposition","attachment; filename*=UTF-8''" + encodedFileName);
+			// 直接使用ZIP流输出(优化点2:内存流式处理)
+			try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
+				zos.setMethod(ZipOutputStream.DEFLATED); // 启用压缩
+				zos.setLevel(Deflater.BEST_SPEED);       // 平衡速度与压缩率
+				// 遍历处理每个附件(优化点3:边下载边压缩)
+				for (String attachmentId : attachmentIds) {
+					processAttachment(attachmentId.trim(), zos, fileNameCounter);
 				}
 			}
-			// 压缩目录
-			ZipUtil.zip(realFilePath, true);  // 自动删除原目录
-			String zipFileName = folderName + ".zip";
-			zipFilePath = rootRealPath + File.separator + zipFileName;
-			// 发送压缩包
-			RequestUtil.downLoadFile(this.getRequest(),this.getResponse(),zipFilePath,zipFileName);
 		} catch (Exception e) {
-			logger.error("/upload/downloadUse", e);
-		} finally {
-			// 清理残留文件(双重保障)
-			if (realFilePath != null) {
-				FileUtil.deleteDir(new File(realFilePath));
-			}
-			if (zipFilePath != null) {
-				FileUtil.deleteFile(zipFilePath);
+			logger.error("压缩包生成失败", e);
+			this.getResponse().setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * 处理单个附件并写入ZIP流
+	 */
+	private void processAttachment(String attachmentId,ZipOutputStream zos,Map<String, AtomicInteger> fileNameCounter) {
+		try {
+			this.getUploadService();
+			FileInfo fileInfo = uploadService.downloadFile(attachmentId);
+			if (fileInfo == null || fileInfo.getFileBytes() == null) {
+				logger.warn("附件不存在: {}", attachmentId);
+				return;
 			}
+			// 生成安全文件名(优化点4:线程安全计数器)
+			String originalName = UploadUtil.getFileName(fileInfo.getFileName(), fileInfo.getExt());
+			String safeName = generateUniqueName(originalName, fileNameCounter);
+			// 创建ZIP条目并写入数据(优化点5:内存流处理)
+			ZipEntry entry = new ZipEntry(safeName);
+			entry.setComment("AttachmentID: " + attachmentId); // 可选元数据
+			zos.putNextEntry(entry);
+			zos.write(fileInfo.getFileBytes());
+			zos.closeEntry();
+		} catch (Exception e) {
+			logger.error("附件处理失败: {}", attachmentId, e);
 		}
 	}
 
 	/**
 	 * 生成唯一文件名(带序号)
 	 */
-	private String generateUniqueName(String originalName, Map<String, Integer> counter) {
-		// 处理特殊字符
+	private String generateUniqueName(String originalName,Map<String, AtomicInteger> counter) {
+		// 清理非法字符
 		String safeName = originalName.replaceAll("[\\\\/:*?\"<>|]", "_");
-		// 处理重复文件名
-		int count = counter.getOrDefault(safeName, 0);
-		counter.put(safeName, count + 1);
-		if (count == 0) {
+		// 原子操作保证线程安全
+		AtomicInteger count = counter.computeIfAbsent(safeName, k -> new AtomicInteger(0));
+		int currentCount = count.getAndIncrement();
+		if (currentCount == 0) {
 			return safeName;
 		} else {
 			int dotIndex = safeName.lastIndexOf('.');
 			if (dotIndex > 0) {
-				String base = safeName.substring(0, dotIndex);
-				String ext = safeName.substring(dotIndex);
-				return base + "(" + count + ")" + ext;
+				return safeName.substring(0, dotIndex)
+						+ "(" + currentCount + ")"
+						+ safeName.substring(dotIndex);
 			}
-			return safeName + "(" + count + ")";
+			return safeName + "(" + currentCount + ")";
 		}
 	}