package com.lxzn.webservice.controller; import com.lxzn.framework.domain.webservice.request.ThirdDeProgramSource; import com.lxzn.framework.domain.webservice.request.PlmProgramSource; import com.lxzn.webservice.DncWebService; import com.lxzn.webservice.ext.MesResultModel; import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import com.alibaba.fastjson.JSONObject; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @RestController @Slf4j @RequestMapping("/plmOuter") public class DncWebController { @Autowired private DncWebService dncWebService; @Value("${fileHomePath}") private String filePath; // 定义关键文件类型 private static final String JSON_EXT = ".json"; private static final String TXT_EXT = ".txt"; /** * 集成plm数据,压缩包(里面是NC压缩包与JSON文件) * @param file * @return */ @PostMapping(value = "/syncPlmNcLogProgram", consumes = "multipart/form-data") @Transactional public String processNcPackage(MultipartFile file) { Path workDir = null; try { if (file == null || file.isEmpty()) { log.error("接收到的压缩文件为空"); return MesResultModel.error("接收到的压缩文件为空"); } // 创建临时目录 String timestamp = String.valueOf(System.currentTimeMillis()); workDir = Paths.get(filePath, "plm_" + timestamp); Files.createDirectories(workDir); // 保存原始文件 Path originalPath = workDir.resolve( Objects.requireNonNull(file.getOriginalFilename()) ); Files.copy(file.getInputStream(), originalPath, StandardCopyOption.REPLACE_EXISTING); log.info("原始文件已保存: {} (大小: {} 字节)", originalPath, Files.size(originalPath)); // 解析外层压缩包 Map packageContents = extractOuterZipContents(originalPath); log.info("外层压缩包解析成功,包含 {} 个文件", packageContents.size()); // 不再查找单个ZIP,改为查找所有ZIP文件 byte[] jsonData = findFileByExtension(packageContents, JSON_EXT); // 使用扩展名".zip"查找所有ZIP压缩包 List zipFiles = findAllFilesByExtension(packageContents, ".zip"); // 检查是否存在至少一个ZIP文件 if (jsonData == null || zipFiles.isEmpty()) { String message = "未找到必要文件: " + (jsonData == null ? "JSON文件" : "") + (zipFiles.isEmpty() ? (jsonData == null ? " 和 " : "") + "NC程序压缩包" : ""); return MesResultModel.error(message); } // 解析JSON String jsonFileName = getFileNameByExtension(packageContents); PlmProgramSource plmPrograms = parseJsonFile(jsonData); log.info("解析JSON文件成功: {}", jsonFileName); // 创建Map存储所有NC文件(文件名->内容) Map allNcFiles = new HashMap<>(); //遍历并解压所有找到的ZIP文件 for (byte[] zipData : zipFiles) { Map ncFiles = extractInnerZipContents(zipData); // 合并文件并检查重复 for (Map.Entry entry : ncFiles.entrySet()) { String fileName = entry.getKey(); if (allNcFiles.containsKey(fileName)) { log.warn("检测到重复NC文件名: {}", fileName); } allNcFiles.put(fileName, entry.getValue()); } } log.info("共解析 {} 个内部压缩包,合并得到 {} 个NC文件", zipFiles.size(), allNcFiles.size()); // 设置NC文件集合 plmPrograms.setNcfiles(allNcFiles); plmPrograms.setFilePath(originalPath.getParent().toString()); plmPrograms.setFileName(originalPath.toString()); // 业务处理 return JSONObject.toJSONString( dncWebService.setPlmTree(plmPrograms) ); } catch (Exception e) { log.error("处理NC文件包失败", e); return MesResultModel.error("文件处理失败: " + e.getMessage()); } finally { // 清理临时文件 if (workDir != null) { deleteDirectory(workDir.toFile()); } } } /** * 集成3DE数据,压缩包(单个压缩包包含多个NC文件、一个工艺数据.TXT) * @param file 上传的压缩包文件 * @return 处理结果 */ @PostMapping(value = "/integration3DEData", consumes = "multipart/form-data") @Transactional public String integration3DEData(MultipartFile file) { Path workDir = null; try { if (file == null || file.isEmpty()) { log.error("接收到的3DE压缩文件为空"); return MesResultModel.error("接收到的3DE压缩文件为空"); } // 创建临时工作目录 String timestamp = String.valueOf(System.currentTimeMillis()); workDir = Paths.get(filePath, "3de_" + timestamp); Files.createDirectories(workDir); // 保存原始文件 Path originalPath = workDir.resolve( Objects.requireNonNull(file.getOriginalFilename()) ); Files.copy(file.getInputStream(), originalPath, StandardCopyOption.REPLACE_EXISTING); log.info("3DE原始文件已保存: {} (大小: {} 字节)", originalPath, Files.size(originalPath)); // 解压ZIP包获取所有文件 Map packageContents = extractOuterZipContents(originalPath); log.info("3DE压缩包解析成功,包含 {} 个文件", packageContents.size()); // 查找工艺数据TXT文件 byte[] processData = findFileByExtension(packageContents, TXT_EXT); if (processData == null) { return MesResultModel.error("未找到工艺数据TXT文件"); } // 解析工艺数据TXT文件 ThirdDeProgramSource thirdDeProgramSource = parseProcessTxtFile(processData); if (thirdDeProgramSource == null) { return MesResultModel.error("解析工艺数据TXT文件失败"); } // 提取所有NC文件 Map ncFiles = extractNcFiles(packageContents); if (ncFiles.isEmpty()) { return MesResultModel.error("未找到NC程序文件"); } log.info("找到 {} 个NC程序文件", ncFiles.size()); // 设置NC文件集合和文件路径信息 thirdDeProgramSource.setNcFiles(ncFiles); thirdDeProgramSource.setFilePath(workDir.toString()); thirdDeProgramSource.setFileName(originalPath.getFileName().toString()); // 调用服务处理3DE数据 return JSONObject.toJSONString( dncWebService.processThirdDEProgram(thirdDeProgramSource) ); } catch (Exception e) { log.error("处理3DE数据包失败", e); return MesResultModel.error("3DE文件处理失败: " + e.getMessage()); } finally { // 清理临时文件 if (workDir != null) { deleteDirectory(workDir.toFile()); } } } /** * 解析工艺数据TXT文件 * @param txtData TXT文件内容 * @return ThirdDeProgramSource对象 */ private ThirdDeProgramSource parseProcessTxtFile(byte[] txtData) { try { String content = new String(txtData, StandardCharsets.UTF_8); log.info("工艺数据TXT内容:\n{}", content); // 使用FastJSON解析JSON格式的TXT JSONObject json = JSONObject.parseObject(content); if (json == null) { log.error("解析工艺数据失败: 不是有效的JSON格式"); return null; } // 创建对象并设置属性 ThirdDeProgramSource programSource = new ThirdDeProgramSource(); programSource.setPartName(json.getString("part_name")); programSource.setPartNo(json.getString("part_no")); programSource.setRevisionNo(json.getString("revision_no")); programSource.setSkgxId(json.getString("skgx_id")); programSource.setSkgxName(json.getString("skgx_name")); programSource.setNcJcid(json.getString("nc_jcid")); programSource.setNcOs(json.getString("nc_os")); programSource.setEquipmentId(json.getString("equipment_id")); programSource.setNcFileName(json.getString("nc_file_name")); programSource.setNcPlantNo(json.getString("nc_plant_no")); programSource.setNcPlantName(json.getString("nc_plant_name")); log.debug("解析的工艺数据: partName={}, partNo={}, revisionNo={}, skgxId={}, skgxName={}, " + "ncJcid={}, ncOs={}, equipmentId={}, ncFileName={}, ncPlantNo={}, ncPlantName={}", programSource.getPartName(), programSource.getPartNo(), programSource.getRevisionNo(), programSource.getSkgxId(), programSource.getSkgxName(), programSource.getNcJcid(), programSource.getNcOs(), programSource.getEquipmentId(), programSource.getNcFileName(), programSource.getNcPlantNo(), programSource.getNcPlantName()); return programSource; } catch (Exception e) { log.error("解析工艺数据TXT文件失败", e); return null; } } /** * 解析键值对格式的TXT文件 * @param content TXT文件内容 * @return 键值对映射 */ private Map parseKeyValueTxt(String content) { Map result = new HashMap<>(); String[] lines = content.split("\\r?\\n"); for (String line : lines) { line = line.trim(); if (line.isEmpty() || line.startsWith("#") || line.startsWith("//")) { continue; // 跳过空行和注释 } int separatorIndex = line.indexOf('='); if (separatorIndex > 0) { String key = line.substring(0, separatorIndex).trim(); String value = line.substring(separatorIndex + 1).trim(); result.put(key, value); } } return result; } /** * 从文件内容中提取NC文件 * @param packageContents 包内容映射 * @return NC文件映射 */ private Map extractNcFiles(Map packageContents) { Map ncFiles = new HashMap<>(); for (Map.Entry entry : packageContents.entrySet()) { String fileName = entry.getKey(); String fileExt = FilenameUtils.getExtension(fileName).toLowerCase(); if (fileExt.equals("nc")) { ncFiles.put(fileName, entry.getValue()); log.debug("找到NC文件: {}", fileName); } } return ncFiles; } // 优化后的解压方法(使用commons-compress) private Map extractOuterZipContents(Path filePath) throws IOException { Map contents = new HashMap<>(); log.info("开始解析ZIP文件: {}", filePath); try { try (ZipFile zipFile = new ZipFile(filePath.toFile())) { log.info("ZIP文件格式检测: {}", zipFile.getEncoding()); Enumeration entries = zipFile.getEntries(); while (entries.hasMoreElements()) { ZipArchiveEntry entry = entries.nextElement(); if (entry.isDirectory()) continue; try (InputStream is = zipFile.getInputStream(entry)) { byte[] data = IOUtils.toByteArray(is); contents.put(entry.getName(), data); log.debug("提取文件: {} ({} 字节)", entry.getName(), data.length); } } } log.info("Apache Commons解析成功"); } catch (Exception e) { log.warn("Apache Commons解析失败,尝试标准库回退: {}", e.getMessage()); try (InputStream is = Files.newInputStream(filePath); ZipInputStream zis = new ZipInputStream(is, StandardCharsets.ISO_8859_1)) { ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { if (entry.isDirectory()) continue; ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[8192]; int len; while ((len = zis.read(buffer)) > 0) { baos.write(buffer, 0, len); } String name = entry.getName(); contents.put(name, baos.toByteArray()); log.debug("回退提取文件: {}", name); } } catch (Exception ex) { throw new IOException("无法解析ZIP文件: " + filePath, ex); } } return contents; } private List findAllFilesByExtension(Map contents, String extension) { String extLower = extension.toLowerCase(); List result = new ArrayList<>(); for (Map.Entry entry : contents.entrySet()) { String fileName = entry.getKey().toLowerCase(); if (fileName.endsWith(extLower)) { result.add(entry.getValue()); } } return result; } // 内部ZIP解析方法 private Map extractInnerZipContents(byte[] zipData) throws IOException { Map files = new HashMap<>(); try (ByteArrayInputStream bais = new ByteArrayInputStream(zipData); ZipInputStream zis = new ZipInputStream(bais)) { ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { if (entry.isDirectory()) continue; ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[8192]; int len; while ((len = zis.read(buffer)) != -1) { baos.write(buffer, 0, len); } files.put(entry.getName(), baos.toByteArray()); } } return files; } /** * 根据扩展名查找文件 */ private byte[] findFileByExtension(Map contents, String extension) { String extLower = extension.toLowerCase(); for (Map.Entry entry : contents.entrySet()) { if (entry.getKey().toLowerCase().endsWith(extLower)) { return entry.getValue(); } } return null; } /** * 获取文件名(根据扩展名) */ private String getFileNameByExtension(Map contents) { for (String key : contents.keySet()) { if (key.toLowerCase().endsWith(JSON_EXT)) { return key; } } return "Unknown"; } /** * 解析JSON文件内容 */ private PlmProgramSource parseJsonFile(byte[] jsonData) { String json = new String(jsonData, StandardCharsets.UTF_8); return JSONObject.parseObject(json, PlmProgramSource.class); } /** * 递归删除目录 */ private void deleteDirectory(File directory) { if (!directory.exists()) return; File[] files = directory.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { deleteDirectory(file); } else { if (!file.delete()) { log.warn("无法删除文件: {}", file.getAbsolutePath()); } } } } if (!directory.delete()) { log.warn("无法删除目录: {}", directory.getAbsolutePath()); } } }