lyh
5 天以前 fda571324684bc8d0945b3e2841305b1207fc3e9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
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);
    }
 
}