From 5ff8c946aa9d77a219019558471507748d285186 Mon Sep 17 00:00:00 2001 From: xy <748613696@qq.com> Date: Fri, 9 Jan 2026 16:57:22 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=84=E6=80=81=E6=96=B0=E5=A2=9E=E5=8E=8B?= =?UTF-8?q?=E7=BC=A9=E5=8C=85=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cssystem/pojo/param/ElementParam.java | 6 +- .../njcn/cssystem/utils/SvgZipProcessor.java | 566 ++++++++++++++++++ .../controller/zutai/ElementController.java | 21 + .../cssystem/service/IElementService.java | 9 + .../service/impl/CsElementServiceImpl.java | 116 +++- 5 files changed, 707 insertions(+), 11 deletions(-) create mode 100644 cs-system/cs-system-api/src/main/java/com/njcn/cssystem/utils/SvgZipProcessor.java diff --git a/cs-system/cs-system-api/src/main/java/com/njcn/cssystem/pojo/param/ElementParam.java b/cs-system/cs-system-api/src/main/java/com/njcn/cssystem/pojo/param/ElementParam.java index 8845903..9c89689 100644 --- a/cs-system/cs-system-api/src/main/java/com/njcn/cssystem/pojo/param/ElementParam.java +++ b/cs-system/cs-system-api/src/main/java/com/njcn/cssystem/pojo/param/ElementParam.java @@ -5,7 +5,7 @@ import lombok.Data; import org.springframework.web.multipart.MultipartFile; import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; +import java.io.File; /** * 类的介绍: @@ -35,11 +35,13 @@ public class ElementParam { private String elementMark; @ApiModelProperty(value = "图元文件") - @NotNull(message="图元文件不能为空!") private MultipartFile multipartFile; @ApiModelProperty(value = "图元类型") @NotBlank(message="图元类型不能为空!") private String elementForm; + @ApiModelProperty(value = "压缩包文件") + private MultipartFile zipFile; + } diff --git a/cs-system/cs-system-api/src/main/java/com/njcn/cssystem/utils/SvgZipProcessor.java b/cs-system/cs-system-api/src/main/java/com/njcn/cssystem/utils/SvgZipProcessor.java new file mode 100644 index 0000000..1147a53 --- /dev/null +++ b/cs-system/cs-system-api/src/main/java/com/njcn/cssystem/utils/SvgZipProcessor.java @@ -0,0 +1,566 @@ +package com.njcn.cssystem.utils; + +import com.njcn.common.pojo.exception.BusinessException; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * SVG压缩包处理器 + * 专门处理ZIP压缩包中的SVG文件 + */ +@Component +public class SvgZipProcessor { + + // SVG文件头检查 + private static final String SVG_HEADER = " ALLOWED_EXTENSIONS = new HashSet<>( + Arrays.asList(".svg", ".svgz") + ); + + /** + * 方法1:验证并提取所有SVG文件 + * 返回格式:Map<文件名, InputStream> + */ + public static Map extractValidSvgFiles(MultipartFile zipFile) throws IOException, InvalidSvgException { + Map svgFiles = new LinkedHashMap<>(); + ZipArchiveInputStream zis = null; + ZipArchiveEntry entry = null; + try { + // 使用支持编码检测的ZipArchiveInputStream + zis = new ZipArchiveInputStream(zipFile.getInputStream(), "UTF-8", true); + byte[] buffer = new byte[8192]; + while ((entry = zis.getNextZipEntry()) != null) { + if (!entry.isDirectory() && !isSvgFile(entry.getName())) { + throw new BusinessException("文件格式错误: " + entry.getName()); + } + if (!entry.isDirectory() && isSvgFile(entry.getName())) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int len; + while ((len = zis.read(buffer)) > 0) { + baos.write(buffer, 0, len); + } + byte[] fileData = baos.toByteArray(); + String fileName = entry.getName(); + // 验证SVG文件 + if (validateSvgFile(fileName, fileData)) { + ByteArrayInputStream svgStream = new ByteArrayInputStream(fileData); + svgFiles.put(fileName, svgStream); + } else { + throw new InvalidSvgException("无效的SVG文件: " + fileName); + } + baos.close(); + } + } + } catch (BusinessException e) { + throw new BusinessException("文件格式错误: " + entry.getName()); + } catch (Exception e) { + throw new IOException("处理ZIP文件时出错", e); + } finally { + // 确保流被关闭 + if (zis != null) { + try { + zis.close(); + } catch (IOException e) { + // 记录日志但不影响主要逻辑 + System.err.println("关闭ZipArchiveInputStream时出错: " + e.getMessage()); + } + } + } + if (svgFiles.isEmpty()) { + throw new InvalidSvgException("压缩包中没有找到有效的SVG文件"); + } + return svgFiles; + } + + /** + * 方法2:流式处理SVG文件 + */ + public void processSvgFilesStreaming(MultipartFile zipFile, + BiConsumer processor) + throws IOException, InvalidSvgException { + + List invalidFiles = new ArrayList<>(); + + try (ZipInputStream zis = new ZipInputStream(zipFile.getInputStream())) { + ZipEntry entry; + byte[] buffer = new byte[8192]; + + while ((entry = zis.getNextEntry()) != null) { + if (!entry.isDirectory() && isSvgFile(entry.getName())) { + + // 读取并验证 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int len; + while ((len = zis.read(buffer)) > 0) { + baos.write(buffer, 0, len); + } + + byte[] fileData = baos.toByteArray(); + + if (validateSvgFile(entry.getName(), fileData)) { + ByteArrayInputStream svgStream = new ByteArrayInputStream(fileData); + processor.accept(entry.getName(), svgStream); + svgStream.close(); + } else { + invalidFiles.add(entry.getName()); + } + + baos.close(); + } + zis.closeEntry(); + } + } + + // 如果有无效文件,抛出异常 + if (!invalidFiles.isEmpty()) { + throw new InvalidSvgException("发现无效的SVG文件: " + invalidFiles); + } + } + + /** + * 方法3:严格的SVG验证和处理 + */ + public List processAndValidateSvgFiles(MultipartFile zipFile, + ValidationOptions options) + throws IOException, InvalidSvgException { + + List svgFiles = new ArrayList<>(); + List validationErrors = new ArrayList<>(); + + try (ZipInputStream zis = new ZipInputStream(zipFile.getInputStream())) { + ZipEntry entry; + byte[] buffer = new byte[8192]; + + while ((entry = zis.getNextEntry()) != null) { + String fileName = entry.getName(); + + // 跳过目录和非SVG文件 + if (entry.isDirectory() || !isSvgFile(fileName)) { + if (!entry.isDirectory() && options.isStrictMode()) { + validationErrors.add("非SVG文件: " + fileName); + } + continue; + } + + // 读取文件内容 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int len; + while ((len = zis.read(buffer)) > 0) { + baos.write(buffer, 0, len); + } + + byte[] fileData = baos.toByteArray(); + + // 执行验证 + ValidationResult result = validateSvgWithDetails(fileName, fileData, options); + + if (result.isValid()) { + SvgFileInfo svgInfo = new SvgFileInfo( + fileName, + new ByteArrayInputStream(fileData), + fileData.length, + entry.getTime(), + result.getSvgAttributes() + ); + svgFiles.add(svgInfo); + } else { + validationErrors.add(fileName + ": " + result.getErrorMessage()); + } + + baos.close(); + zis.closeEntry(); + } + } + + // 检查验证结果 + if (!validationErrors.isEmpty()) { + throw new InvalidSvgException("SVG验证失败:\n" + + String.join("\n", validationErrors)); + } + + if (svgFiles.isEmpty()) { + throw new InvalidSvgException("压缩包中没有有效的SVG文件"); + } + + return svgFiles; + } + + /** + * 方法4:仅获取SVG文件名 + */ + public List getSvgFileNames(MultipartFile zipFile) throws IOException { + List svgNames = new ArrayList<>(); + + try (ZipInputStream zis = new ZipInputStream(zipFile.getInputStream())) { + ZipEntry entry; + + while ((entry = zis.getNextEntry()) != null) { + if (!entry.isDirectory() && isSvgFile(entry.getName())) { + svgNames.add(entry.getName()); + } + zis.closeEntry(); + } + } + + return svgNames; + } + + /** + * 验证是否为SVG文件(基于扩展名) + */ + public static boolean isSvgFile(String fileName) { + if (fileName == null) return false; + + String lowerName = fileName.toLowerCase(); + return ALLOWED_EXTENSIONS.stream() + .anyMatch(lowerName::endsWith); + } + + /** + * 基本的SVG文件验证 + */ + private static boolean validateSvgFile(String fileName, byte[] fileData) { + try { + // 转换为字符串进行检查 + String content = new String(fileData, StandardCharsets.UTF_8); + content = content.trim(); + + // 检查文件大小 + if (fileData.length == 0) { + return false; + } + + // 检查是否包含XML声明和SVG标签 + boolean hasXmlHeader = content.startsWith(SVG_HEADER); + boolean hasSvgTag = content.contains(SVG_START_TAG); + + // 简单验证:要么有XML声明+SVG标签,要么直接以 options.getMaxFileSize()) { + result.setError("文件大小超过限制: " + fileData.length + " bytes"); + return result; + } + + if (fileData.length == 0) { + result.setError("文件为空"); + return result; + } + + // 2. 检查基本结构 + boolean hasXmlHeader = content.startsWith(SVG_HEADER); + boolean hasSvgTag = content.contains(SVG_START_TAG); + + if (!hasSvgTag) { + result.setError("缺少SVG标签"); + return result; + } + + // 3. 如果开启了XML验证,进行严格的XML解析 + if (options.isValidateXml()) { + if (!isValidXml(content)) { + result.setError("无效的XML结构"); + return result; + } + + // 提取SVG属性 + Map attributes = extractSvgAttributes(content); + result.setSvgAttributes(attributes); + + // 检查SVG尺寸 + if (options.isCheckDimensions()) { + String width = attributes.get("width"); + String height = attributes.get("height"); + + if (width != null && height != null) { + try { + // 移除单位(px, em, %等) + width = width.replaceAll("[^0-9.]", ""); + height = height.replaceAll("[^0-9.]", ""); + + double w = Double.parseDouble(width); + double h = Double.parseDouble(height); + + if (w > options.getMaxWidth() || h > options.getMaxHeight()) { + result.setError(String.format("尺寸过大: %.0fx%.0f", w, h)); + return result; + } + + if (w < options.getMinWidth() || h < options.getMinHeight()) { + result.setError(String.format("尺寸过小: %.0fx%.0f", w, h)); + return result; + } + } catch (NumberFormatException e) { + // 如果无法解析尺寸,可能是使用百分比等单位 + } + } + } + } + + // 4. 检查是否有潜在危险元素(安全考虑) + if (options.isCheckSecurity()) { + if (containsDangerousElements(content)) { + result.setError("包含潜在危险元素"); + return result; + } + } + + result.setValid(true); + + } catch (Exception e) { + result.setError("验证异常: " + e.getMessage()); + } + + return result; + } + + /** + * 检查是否为有效的XML + */ + private boolean isValidXml(String content) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + // 安全配置,防止XXE攻击 + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setXIncludeAware(false); + factory.setExpandEntityReferences(false); + + DocumentBuilder builder = factory.newDocumentBuilder(); + builder.parse(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); + return true; + } catch (ParserConfigurationException | SAXException | IOException e) { + return false; + } + } + + /** + * 提取SVG属性 + */ + private Map extractSvgAttributes(String content) { + Map attributes = new HashMap<>(); + + try { + int startIndex = content.indexOf("', startIndex); + if (endIndex == -1) return attributes; + + String svgTag = content.substring(startIndex, endIndex); + + // 简单解析属性 + String[] parts = svgTag.split("\\s+"); + for (String part : parts) { + if (part.contains("=")) { + String[] keyValue = part.split("=", 2); + if (keyValue.length == 2) { + String key = keyValue[0].trim(); + String value = keyValue[1].trim().replaceAll("[\"']", ""); + attributes.put(key, value); + } + } + } + } catch (Exception e) { + // 解析失败,返回空map + } + + return attributes; + } + + /** + * 检查是否包含危险元素 + */ + private boolean containsDangerousElements(String content) { + String lowerContent = content.toLowerCase(); + + // 检查潜在的脚本标签 + if (lowerContent.contains(" svgAttributes; + + public SvgFileInfo(String fileName, InputStream inputStream, long fileSize, + long lastModified, Map svgAttributes) { + this.fileName = fileName; + this.inputStream = inputStream; + this.fileSize = fileSize; + this.lastModified = lastModified; + this.svgAttributes = svgAttributes; + } + + // Getters + public String getFileName() { return fileName; } + public InputStream getInputStream() { return inputStream; } + public long getFileSize() { return fileSize; } + public long getLastModified() { return lastModified; } + public Map getSvgAttributes() { return svgAttributes; } + + public String getBaseName() { + int dotIndex = fileName.lastIndexOf('.'); + return dotIndex > 0 ? fileName.substring(0, dotIndex) : fileName; + } + + public String getExtension() { + return getFileExtension(fileName); + } + } + + /** + * 验证选项类 + */ + public class ValidationOptions { + private boolean strictMode = true; // 严格模式,不允许非SVG文件 + private boolean validateXml = true; // 验证XML结构 + private boolean checkDimensions = true; // 检查尺寸 + private boolean checkSecurity = true; // 安全检查 + private long maxFileSize = 10 * 1024 * 1024; // 最大文件大小(10MB) + private double maxWidth = 5000; // 最大宽度 + private double maxHeight = 5000; // 最大高度 + private double minWidth = 10; // 最小宽度 + private double minHeight = 10; // 最小高度 + + // Getters and Setters + public boolean isStrictMode() { return strictMode; } + public void setStrictMode(boolean strictMode) { this.strictMode = strictMode; } + + public boolean isValidateXml() { return validateXml; } + public void setValidateXml(boolean validateXml) { this.validateXml = validateXml; } + + public boolean isCheckDimensions() { return checkDimensions; } + public void setCheckDimensions(boolean checkDimensions) { this.checkDimensions = checkDimensions; } + + public boolean isCheckSecurity() { return checkSecurity; } + public void setCheckSecurity(boolean checkSecurity) { this.checkSecurity = checkSecurity; } + + public long getMaxFileSize() { return maxFileSize; } + public void setMaxFileSize(long maxFileSize) { this.maxFileSize = maxFileSize; } + + public double getMaxWidth() { return maxWidth; } + public void setMaxWidth(double maxWidth) { this.maxWidth = maxWidth; } + + public double getMaxHeight() { return maxHeight; } + public void setMaxHeight(double maxHeight) { this.maxHeight = maxHeight; } + + public double getMinWidth() { return minWidth; } + public void setMinWidth(double minWidth) { this.minWidth = minWidth; } + + public double getMinHeight() { return minHeight; } + public void setMinHeight(double minHeight) { this.minHeight = minHeight; } + } + + /** + * 验证结果类 + */ + public class ValidationResult { + private final String fileName; + private boolean valid; + private String errorMessage; + private Map svgAttributes = new HashMap<>(); + + public ValidationResult(String fileName) { + this.fileName = fileName; + this.valid = false; + } + + // Getters and Setters + public String getFileName() { return fileName; } + public boolean isValid() { return valid; } + public void setValid(boolean valid) { this.valid = valid; } + + public String getErrorMessage() { return errorMessage; } + public void setError(String errorMessage) { + this.errorMessage = errorMessage; + this.valid = false; + } + + public Map getSvgAttributes() { return svgAttributes; } + public void setSvgAttributes(Map svgAttributes) { + this.svgAttributes = svgAttributes; + } + } + + /** + * 自定义异常类 + */ + public static class InvalidSvgException extends Exception { + public InvalidSvgException(String message) { + super(message); + } + + public InvalidSvgException(String message, Throwable cause) { + super(message, cause); + } + } +} \ No newline at end of file diff --git a/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/controller/zutai/ElementController.java b/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/controller/zutai/ElementController.java index 4d13c2e..bac03f5 100644 --- a/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/controller/zutai/ElementController.java +++ b/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/controller/zutai/ElementController.java @@ -19,6 +19,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.util.List; /** @@ -64,4 +66,23 @@ public class ElementController extends BaseController { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); } + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/addByZip") + @ApiOperation("压缩包新增图元") + public HttpResult> addByZip(@Validated ElementParam param){ + String methodDescribe = getMethodDescribe("addByZip"); + List list = csElementService.addByZip(param); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/downloadByZip") + @ApiOperation("压缩包下载图元") + @ApiImplicitParam(name = "name", value = "组件分类名称", required = true) + public HttpResult downloadByZip(@RequestParam("name") String name, HttpServletRequest request, HttpServletResponse response){ + String methodDescribe = getMethodDescribe("downloadByZip"); + HttpServletResponse resp = csElementService.downloadZip(name, request, response); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, resp, methodDescribe); + } + } diff --git a/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/service/IElementService.java b/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/service/IElementService.java index 4318bac..5f8ad5f 100644 --- a/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/service/IElementService.java +++ b/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/service/IElementService.java @@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.njcn.cssystem.pojo.param.ElementParam; import com.njcn.cssystem.pojo.po.CsElement; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.util.List; /** @@ -32,4 +34,11 @@ public interface IElementService extends IService { */ void deleteById(String id); + /** + * + */ + List addByZip(ElementParam param); + + HttpServletResponse downloadZip(String name, HttpServletRequest request, HttpServletResponse response); + } diff --git a/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/service/impl/CsElementServiceImpl.java b/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/service/impl/CsElementServiceImpl.java index 2403b16..c077589 100644 --- a/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/service/impl/CsElementServiceImpl.java +++ b/cs-system/cs-system-boot/src/main/java/com/njcn/cssystem/service/impl/CsElementServiceImpl.java @@ -1,20 +1,35 @@ package com.njcn.cssystem.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.njcn.common.pojo.exception.BusinessException; -import com.njcn.cssystem.enums.CsSystemResponseEnum; import com.njcn.cssystem.mapper.CsElementMapper; import com.njcn.cssystem.pojo.param.ElementParam; import com.njcn.cssystem.pojo.po.CsElement; import com.njcn.cssystem.service.IElementService; +import com.njcn.cssystem.utils.SvgZipProcessor; import com.njcn.oss.constant.OssPath; import com.njcn.oss.utils.FileStorageUtil; import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.util.ArrayList; import java.util.List; -import java.util.Objects; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.zip.Adler32; +import java.util.zip.CheckedOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** *

@@ -24,21 +39,23 @@ import java.util.Objects; * @author xuyang * @since 2023-07-12 */ +@Slf4j @Service @AllArgsConstructor public class CsElementServiceImpl extends ServiceImpl implements IElementService { private final FileStorageUtil fileStorageUtil; + private final SvgZipProcessor svgZipProcessor; @Override public CsElement addElement(ElementParam param) { - CsElement po = this.lambdaQuery().eq(CsElement::getStatus,1) - .eq(CsElement::getElementCode,param.getElementCode()) - .eq(CsElement::getElementName,param.getElementName()) - .eq(CsElement::getElementMark,param.getElementMark()).one(); - if (!Objects.isNull(po)){ - throw new BusinessException(CsSystemResponseEnum.SAME_DATA_ERROR); - } +// CsElement po = this.lambdaQuery().eq(CsElement::getStatus,1) +// .eq(CsElement::getElementCode,param.getElementCode()) +// .eq(CsElement::getElementName,param.getElementName()) +// .eq(CsElement::getElementMark,param.getElementMark()).one(); +// if (!Objects.isNull(po)){ +// throw new BusinessException(CsSystemResponseEnum.SAME_DATA_ERROR); +// } CsElement csElement = new CsElement(); BeanUtils.copyProperties(param,csElement); String path = fileStorageUtil.uploadMultipart(param.getMultipartFile(), OssPath.ELEMENT); @@ -61,4 +78,85 @@ public class CsElementServiceImpl extends ServiceImpl addByZip(ElementParam param) { + List list = new ArrayList<>(); + try { + // 验证并提取SVG文件 + Map svgFiles = SvgZipProcessor.extractValidSvgFiles(param.getZipFile()); + log.info("提取到 {} 个SVG文件:", svgFiles.size()); + svgFiles.forEach((name, stream) -> { + //获取文件名称 + String[] arr = name.split("/"); + //存储数据库、上传文件服务器 + CsElement csElement = new CsElement(); + BeanUtils.copyProperties(param,csElement); + String path = fileStorageUtil.uploadStreamSpecifyName(stream, OssPath.ELEMENT, arr[arr.length - 1]); + csElement.setPath(path); + csElement.setStatus(1); + csElement.setElementName(arr[arr.length - 1].split("\\.")[0]); + this.save(csElement); + csElement.setPath(path); + list.add(csElement); + }); + } catch (BusinessException e) { + throw new BusinessException(e.getMessage()); + } catch (IOException | SvgZipProcessor.InvalidSvgException e) { + throw new RuntimeException(e); + } + return list; + } + + @Override + public HttpServletResponse downloadZip(String name, HttpServletRequest request, HttpServletResponse response) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(CsElement::getElementSonType,name).eq(CsElement::getStatus,1); + List list = this.list(wrapper); + if(CollectionUtils.isNotEmpty(list)) { + try { + Map filePathMap = list.stream().filter(r -> StringUtils.isNotBlank(r.getPath())).collect(Collectors.toMap(CsElement::getId, CsElement::getPath)); + // 创建临时文件 + File zipFile = File.createTempFile(name, ".zip"); + FileOutputStream f = new FileOutputStream(zipFile); + CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32()); + // 用于将数据压缩成Zip文件格式 + ZipOutputStream zos = new ZipOutputStream(csum); + for (Map.Entry entry : filePathMap.entrySet()) { + CsElement csElement = this.getById(entry.getKey()); + InputStream inputStream = fileStorageUtil.getFileStream(entry.getValue()); + // 对于每一个要被存放到压缩包的文件,都必须调用ZipOutputStream对象的putNextEntry()方法,确保压缩包里面文件不同名 + String path = csElement.getPath(); + zos.putNextEntry(new ZipEntry(csElement.getElementName() + ".svg")); + int bytesRead = 0; + // 向压缩文件中输出数据 + while ((bytesRead = inputStream.read()) != -1) { + zos.write(bytesRead); + } + inputStream.close(); + zos.closeEntry(); // 当前文件写完,定位为写入下一条项目 + } + zos.close(); + InputStream fis = new BufferedInputStream(Files.newInputStream(zipFile.toPath())); + byte[] buffer = new byte[fis.available()]; + fis.read(buffer); + fis.close(); + // 清空response + response.reset(); + // 设置response的Header + response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(name + ".zip", "utf-8")); + response.addHeader("Content-Length", "" + zipFile.length()); + OutputStream toClient = new BufferedOutputStream(response.getOutputStream()); + response.setContentType("application/octet-stream"); + toClient.write(buffer); + toClient.flush(); + toClient.close(); + zipFile.delete(); + } catch (Exception e) { + log.error("下载异常"); + } + } + return response; + } + }