fix: 优化压缩包解析逻辑

This commit is contained in:
tangwei 2026-05-07 16:56:46 +08:00
parent f7eb3a8ab7
commit a6810efe79

View File

@ -73,6 +73,55 @@ public class ZipFileUtil {
return extractZipToTemp(file, getDefaultTempDir()); return extractZipToTemp(file, getDefaultTempDir());
} }
// public static ZipContent extractZipToTemp(MultipartFile file, String baseTempDir) throws IOException {
// ZipContent content = new ZipContent();
//
// String taskId = UUID.randomUUID().toString().substring(0, 8);
// Path tempDirPath = Paths.get(baseTempDir, "zip_" + taskId);
//
// log.info("extractZipToTemp: {}", tempDirPath);
// Files.createDirectories(tempDirPath);
// content.tempDir = tempDirPath.toString();
// File zipFile = new File(tempDirPath.toFile(), "upload.zip");
// file.transferTo(zipFile);
//
// try {
// log.info("--------------isValidZipFile------------");
// if (!isValidZipFile(zipFile)) {
// log.info("--------------文件不是有效的ZIP格式或已损坏------------");
// throw new IOException("文件不是有效的ZIP格式或已损坏");
// }
//
// IOException lastException = null;
// Charset[] charsets = new Charset[]{
// StandardCharsets.UTF_8,
// Charset.forName("GBK"),
// StandardCharsets.ISO_8859_1,
// Charset.forName("GB18030")
// };
//
// for (Charset charset : charsets) {
// try {
// content = extractFromZipFile(zipFile, tempDirPath.toFile(), charset);
// content.tempDir = tempDirPath.toString();
// return content;
// } catch (IOException e) {
// lastException = e;
// content = new ZipContent();
// content.tempDir = tempDirPath.toString();
// }
// }
//
// log.error("extractZipToTemp: {}", lastException.getMessage());
// throw lastException != null ? lastException : new IOException("无法解析ZIP文件");
//
// } finally {
// if (zipFile.exists()) {
// zipFile.delete();
// }
// }
// }
public static ZipContent extractZipToTemp(MultipartFile file, String baseTempDir) throws IOException { public static ZipContent extractZipToTemp(MultipartFile file, String baseTempDir) throws IOException {
ZipContent content = new ZipContent(); ZipContent content = new ZipContent();
@ -102,7 +151,7 @@ public class ZipFileUtil {
for (Charset charset : charsets) { for (Charset charset : charsets) {
try { try {
content = extractFromZipFile(zipFile, tempDirPath.toFile(), charset); content = extractExcelWithRelatedFiles(zipFile, tempDirPath.toFile(), charset);
content.tempDir = tempDirPath.toString(); content.tempDir = tempDirPath.toString();
return content; return content;
} catch (IOException e) { } catch (IOException e) {
@ -179,6 +228,134 @@ public class ZipFileUtil {
return content; return content;
} }
private static ZipContent extractExcelWithRelatedFiles(File zipFile, File tempDir, Charset charset) throws IOException {
ZipContent content = new ZipContent();
try (ZipFile zip = new ZipFile(zipFile, charset)) {
Enumeration<? extends ZipEntry> entries = zip.entries();
String excelEntryName = null;
String excelParentPath = null;
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String entryName = entry.getName();
if (entry.isDirectory()) {
continue;
}
String lowerName = entryName.toLowerCase();
if ((lowerName.endsWith(".xlsx") || lowerName.endsWith(".xls")) && excelEntryName == null) {
excelEntryName = entryName;
excelParentPath = getParentDirectory(entryName);
log.info("找到Excel文件: {}, 所在目录: {}", excelEntryName, excelParentPath);
break;
}
}
if (excelEntryName == null) {
throw new IOException("ZIP文件中未找到Excel文件(.xlsx或.xls)");
}
entries = zip.entries();
int excelCount = 0;
int imageCount = 0;
int videoCount = 0;
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String entryName = entry.getName();
if (entry.isDirectory()) {
continue;
}
String entryParentPath = getParentDirectory(entryName);
String fileName = getFileName(entryName);
String lowerName = entryName.toLowerCase();
boolean shouldExtract = false;
String targetSubFolder = "";
if (lowerName.endsWith(".xlsx") || lowerName.endsWith(".xls")) {
if (excelParentPath.equals(entryParentPath)) {
shouldExtract = true;
targetSubFolder = "";
}
} else if (isInSubDirectory(entryParentPath, excelParentPath, "images")) {
if (isImageFile(lowerName)) {
shouldExtract = true;
targetSubFolder = "images";
}
} else if (isInSubDirectory(entryParentPath, excelParentPath, "videos")) {
if (isVideoFile(lowerName)) {
shouldExtract = true;
targetSubFolder = "videos";
}
}
if (shouldExtract) {
try (InputStream is = zip.getInputStream(entry)) {
String filePath = saveFileToDir(is, tempDir, targetSubFolder, fileName);
if (targetSubFolder.isEmpty()) {
if (content.excelFilePath == null) {
content.excelFileName = fileName;
content.excelFilePath = filePath;
excelCount++;
log.info("提取Excel文件: {}", fileName);
}
} else if ("images".equals(targetSubFolder)) {
content.images.put(fileName, filePath);
imageCount++;
log.debug("提取图片文件: {}", fileName);
} else if ("videos".equals(targetSubFolder)) {
content.videos.put(fileName, filePath);
videoCount++;
log.debug("提取视频文件: {}", fileName);
}
}
}
}
if (content.excelFilePath == null) {
throw new IOException("未能提取到Excel文件");
}
log.info("提取完成 - Excel: {}个, 图片: {}个, 视频: {}个",
excelCount, imageCount, videoCount);
}
return content;
}
private static String getParentDirectory(String entryName) {
if (entryName == null || entryName.isEmpty()) {
return "";
}
int lastSep = Math.max(entryName.lastIndexOf('/'), entryName.lastIndexOf('\\'));
if (lastSep >= 0) {
return entryName.substring(0, lastSep);
}
return "";
}
private static boolean isInSubDirectory(String entryParentPath, String excelParentPath, String subFolder) {
String expectedPath;
if (excelParentPath.isEmpty()) {
expectedPath = subFolder;
} else {
expectedPath = excelParentPath + "/" + subFolder;
}
String normalizedEntryPath = entryParentPath.replace("\\", "/");
String normalizedExpectedPath = expectedPath.replace("\\", "/");
return normalizedEntryPath.equalsIgnoreCase(normalizedExpectedPath);
}
private static String getFileName(String entryName) { private static String getFileName(String entryName) {
if (entryName == null) return ""; if (entryName == null) return "";
int lastSep = Math.max(entryName.lastIndexOf('/'), entryName.lastIndexOf('\\')); int lastSep = Math.max(entryName.lastIndexOf('/'), entryName.lastIndexOf('\\'));