package com.lxzn.webservice.controller;
|
|
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.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.stream.Collectors;
|
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipInputStream;
|
|
@RestController
|
@Slf4j
|
@RequestMapping("/plmOuter")
|
public class DncWebController {
|
|
@Autowired
|
private DncWebService dncWebService;
|
|
@Value("${ncPdm.file_path}")
|
private String filePath;
|
|
// 定义关键文件类型
|
private static final String INNER_ZIP_PREFIX = "【公开】NC程序文件 ";
|
private static final String JSON_EXT = ".json";
|
|
@PostMapping(value = "/syncPlmNcLogProgram", consumes = "multipart/form-data")
|
@Transactional
|
public String processNcPackage(MultipartFile file) {
|
try {
|
if (file == null || file.isEmpty()) {
|
log.error("接收到的压缩文件为空");
|
return MesResultModel.error("接收到的压缩文件为空");
|
}
|
|
// 创建临时目录
|
String timestamp = String.valueOf(System.currentTimeMillis());
|
Path workDir = Paths.get(filePath, 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<String, byte[]> packageContents = extractOuterZipContents(originalPath);
|
log.info("外层压缩包解析成功,包含 {} 个文件", packageContents.size());
|
|
// 不再查找单个ZIP,改为查找所有ZIP文件
|
byte[] jsonData = findFileByExtension(packageContents, JSON_EXT);
|
// 使用扩展名".zip"查找所有ZIP压缩包
|
List<byte[]> 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<String, byte[]> allNcFiles = new HashMap<>();
|
|
//遍历并解压所有找到的ZIP文件
|
for (byte[] zipData : zipFiles) {
|
Map<String, byte[]> ncFiles = extractInnerZipContents(zipData);
|
|
// 合并文件并检查重复
|
for (Map.Entry<String, byte[]> entry : ncFiles.entrySet()) {
|
String fileName = entry.getKey();
|
if (allNcFiles.containsKey(fileName)) {
|
// 修改点5:记录重复警告(实际业务中可能需要更严格处理)
|
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.setTree(plmPrograms)
|
);
|
} catch (Exception e) {
|
log.error("处理NC文件包失败", e);
|
return MesResultModel.error("文件处理失败: " + e.getMessage());
|
}
|
}
|
|
// 优化后的解压方法(使用commons-compress)
|
private Map<String, byte[]> extractOuterZipContents(Path filePath) throws IOException {
|
Map<String, byte[]> contents = new HashMap<>();
|
log.info("开始解析ZIP文件: {}", filePath);
|
|
try {
|
try (ZipFile zipFile = new ZipFile(filePath.toFile())) {
|
log.info("ZIP文件格式检测: {}", zipFile.getEncoding());
|
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
|
|
while (entries.hasMoreElements()) {
|
ZipArchiveEntry entry = entries.nextElement();
|
if (entry.isDirectory()) continue;
|
|
try (InputStream is = zipFile.getInputStream(entry)) {
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
byte[] buffer = new byte[8192];
|
int len;
|
while ((len = is.read(buffer)) > 0) {
|
baos.write(buffer, 0, len);
|
}
|
|
// 保留原始文件名
|
String name = entry.getName();
|
contents.put(name, baos.toByteArray());
|
log.debug("提取文件: {} ({} 字节)", name, baos.size());
|
}
|
}
|
}
|
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<byte[]> findAllFilesByExtension(Map<String, byte[]> contents, String extension) {
|
String extLower = extension.toLowerCase();
|
return contents.entrySet().stream()
|
.filter(e -> {
|
String fileName = e.getKey();
|
// 使用小写比较避免大小写敏感问题
|
return fileName.toLowerCase().endsWith(extLower);
|
})
|
.map(Map.Entry::getValue)
|
.collect(Collectors.toList());
|
}
|
|
// 内部ZIP解析方法
|
private Map<String, byte[]> extractInnerZipContents(byte[] zipData) throws IOException {
|
Map<String, byte[]> 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<String, byte[]> contents, String extension) {
|
return contents.entrySet().stream()
|
.filter(e -> e.getKey().toLowerCase().endsWith(extension))
|
.map(Map.Entry::getValue)
|
.findFirst()
|
.orElse(null);
|
}
|
|
/**
|
* 获取文件名(根据扩展名)
|
*/
|
private String getFileNameByExtension(Map<String, byte[]> contents) {
|
return contents.keySet().stream()
|
.filter(k -> k.toLowerCase().endsWith(DncWebController.JSON_EXT))
|
.findFirst()
|
.orElse("Unknown");
|
}
|
|
/**
|
* 查找与指定前缀匹配的文件内容
|
* @param contents 文件内容映射(文件名 -> 文件内容)
|
* @param prefix 要匹配的文件名前缀
|
* @return 匹配的第一个文件内容,未找到返回 null
|
*/
|
private byte[] findFileByPrefix(Map<String, byte[]> contents, String prefix) {
|
if (prefix == null || prefix.isEmpty()) {
|
log.warn("文件前缀不能为空");
|
return null;
|
}
|
|
return contents.entrySet().stream()
|
.filter(entry -> {
|
String fileName = entry.getKey();
|
|
// 路径规范化处理
|
String normalizedKey = fileName.replace('\\', '/');
|
|
// 获取纯文件名
|
int lastSlash = normalizedKey.lastIndexOf('/');
|
String pureFileName = (lastSlash >= 0) ?
|
normalizedKey.substring(lastSlash + 1) :
|
normalizedKey;
|
|
// 调试日志
|
log.debug("检查文件: [原始: {}], [纯文件名: {}], 前缀: {}",
|
fileName, pureFileName, prefix);
|
|
return pureFileName.contains("NC");
|
})
|
.map(Map.Entry::getValue)
|
.findFirst()
|
.orElse(null);
|
}
|
|
/**
|
* 解析JSON文件内容
|
*/
|
private PlmProgramSource parseJsonFile(byte[] jsonData) {
|
String json = new String(jsonData, StandardCharsets.UTF_8);
|
return JSONObject.parseObject(json, PlmProgramSource.class);
|
}
|
|
}
|