|
@@ -0,0 +1,266 @@
|
|
|
+package com.siwei.apply.utils;
|
|
|
+
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+import java.io.*;
|
|
|
+import java.nio.charset.Charset;
|
|
|
+import java.nio.file.*;
|
|
|
+import java.util.*;
|
|
|
+import java.util.zip.ZipEntry;
|
|
|
+import java.util.zip.ZipFile;
|
|
|
+import java.util.zip.ZipInputStream;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 文件解压工具类
|
|
|
+ */
|
|
|
+public class FileExtractUtil {
|
|
|
+
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(FileExtractUtil.class);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否为压缩文件
|
|
|
+ *
|
|
|
+ * @param filePath 文件路径
|
|
|
+ * @return 是否为压缩文件
|
|
|
+ */
|
|
|
+ public static boolean isCompressedFile(String filePath) {
|
|
|
+ if (filePath == null || filePath.trim().isEmpty()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ String lowerCasePath = filePath.toLowerCase();
|
|
|
+ return lowerCasePath.endsWith(".zip") ||
|
|
|
+ lowerCasePath.endsWith(".rar") ||
|
|
|
+ lowerCasePath.endsWith(".7z");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解压文件到指定目录
|
|
|
+ *
|
|
|
+ * @param compressedFilePath 压缩文件路径
|
|
|
+ * @param extractDir 解压目录路径
|
|
|
+ * @return 是否解压成功
|
|
|
+ */
|
|
|
+ public static boolean extractFile(String compressedFilePath, String extractDir) {
|
|
|
+ try {
|
|
|
+ if (compressedFilePath == null || extractDir == null) {
|
|
|
+ logger.error("解压文件失败:文件路径或解压目录不能为空");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ Path compressedFile = Paths.get(compressedFilePath);
|
|
|
+ if (!Files.exists(compressedFile)) {
|
|
|
+ logger.error("解压文件失败:压缩文件不存在: {}", compressedFilePath);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建解压目录
|
|
|
+ Path extractPath = Paths.get(extractDir);
|
|
|
+ Files.createDirectories(extractPath);
|
|
|
+
|
|
|
+ String lowerCasePath = compressedFilePath.toLowerCase();
|
|
|
+ if (lowerCasePath.endsWith(".zip")) {
|
|
|
+ return extractZipFile(compressedFilePath, extractDir);
|
|
|
+ } else if (lowerCasePath.endsWith(".rar")) {
|
|
|
+ logger.warn("暂不支持RAR格式解压: {}", compressedFilePath);
|
|
|
+ return false;
|
|
|
+ } else if (lowerCasePath.endsWith(".7z")) {
|
|
|
+ logger.warn("暂不支持7Z格式解压: {}", compressedFilePath);
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ logger.error("不支持的压缩格式: {}", compressedFilePath);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("解压文件异常,压缩文件: {}, 解压目录: {}", compressedFilePath, extractDir, e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解压文件到同名文件夹
|
|
|
+ *
|
|
|
+ * @param compressedFilePath 压缩文件路径
|
|
|
+ * @return 解压目录路径,失败返回null
|
|
|
+ */
|
|
|
+ public static String extractToSameNameFolder(String compressedFilePath) {
|
|
|
+ try {
|
|
|
+ if (compressedFilePath == null || compressedFilePath.trim().isEmpty()) {
|
|
|
+ logger.error("解压文件失败:压缩文件路径不能为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ Path compressedFile = Paths.get(compressedFilePath);
|
|
|
+ if (!Files.exists(compressedFile)) {
|
|
|
+ logger.error("解压文件失败:压缩文件不存在: {}", compressedFilePath);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成同名文件夹路径
|
|
|
+ String fileName = compressedFile.getFileName().toString();
|
|
|
+ String baseName = fileName.substring(0, fileName.lastIndexOf('.'));
|
|
|
+ Path extractDir = compressedFile.getParent().resolve(baseName);
|
|
|
+
|
|
|
+ boolean success = extractFile(compressedFilePath, extractDir.toString());
|
|
|
+ if (success) {
|
|
|
+ logger.info("成功解压文件到同名文件夹,压缩文件: {}, 解压目录: {}", compressedFilePath, extractDir);
|
|
|
+ return extractDir.toString();
|
|
|
+ } else {
|
|
|
+ logger.error("解压文件到同名文件夹失败,压缩文件: {}", compressedFilePath);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("解压文件到同名文件夹异常,压缩文件: {}", compressedFilePath, e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解压ZIP文件
|
|
|
+ *
|
|
|
+ * @param zipFilePath ZIP文件路径
|
|
|
+ * @param destDir 目标目录
|
|
|
+ * @return 是否解压成功
|
|
|
+ */
|
|
|
+ private static boolean extractZipFile(String zipFilePath, String destDir) {
|
|
|
+ // 尝试多种编码方式解压ZIP文件
|
|
|
+ Charset[] charsets = {
|
|
|
+ Charset.forName("GBK"), // 中文Windows系统常用编码
|
|
|
+ Charset.forName("GB2312"), // 简体中文编码
|
|
|
+ Charset.forName("UTF-8"), // 标准UTF-8编码
|
|
|
+ Charset.defaultCharset() // 系统默认编码
|
|
|
+ };
|
|
|
+
|
|
|
+ for (Charset charset : charsets) {
|
|
|
+ try {
|
|
|
+ if (extractZipFileWithCharset(zipFilePath, destDir, charset)) {
|
|
|
+ logger.info("成功解压ZIP文件: {} 到目录: {},使用编码: {}", zipFilePath, destDir, charset.name());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.debug("使用编码 {} 解压失败,尝试下一种编码: {}", charset.name(), e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.error("所有编码方式都无法解压ZIP文件: {}", zipFilePath);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用指定编码解压ZIP文件
|
|
|
+ *
|
|
|
+ * @param zipFilePath ZIP文件路径
|
|
|
+ * @param destDir 目标目录
|
|
|
+ * @param charset 字符编码
|
|
|
+ * @return 是否解压成功
|
|
|
+ */
|
|
|
+ private static boolean extractZipFileWithCharset(String zipFilePath, String destDir, Charset charset) throws IOException {
|
|
|
+ try (ZipFile zipFile = new ZipFile(zipFilePath, charset)) {
|
|
|
+ java.util.Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
|
|
+
|
|
|
+ while (entries.hasMoreElements()) {
|
|
|
+ ZipEntry entry = entries.nextElement();
|
|
|
+ Path filePath = Paths.get(destDir, entry.getName());
|
|
|
+
|
|
|
+ // 安全检查:防止目录遍历攻击
|
|
|
+ if (!filePath.normalize().startsWith(Paths.get(destDir).normalize())) {
|
|
|
+ logger.warn("检测到潜在的目录遍历攻击,跳过文件: {}", entry.getName());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (entry.isDirectory()) {
|
|
|
+ Files.createDirectories(filePath);
|
|
|
+ } else {
|
|
|
+ // 确保父目录存在
|
|
|
+ Files.createDirectories(filePath.getParent());
|
|
|
+
|
|
|
+ // 写入文件
|
|
|
+ try (InputStream is = zipFile.getInputStream(entry);
|
|
|
+ OutputStream os = Files.newOutputStream(filePath)) {
|
|
|
+ byte[] buffer = new byte[8192];
|
|
|
+ int length;
|
|
|
+ while ((length = is.read(buffer)) > 0) {
|
|
|
+ os.write(buffer, 0, length);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取目录结构
|
|
|
+ *
|
|
|
+ * @param directory 目录路径
|
|
|
+ * @return 目录结构Map
|
|
|
+ */
|
|
|
+ public static Map<String, Object> getDirectoryStructure(Path directory) {
|
|
|
+ try {
|
|
|
+ if (!Files.exists(directory)) {
|
|
|
+ logger.warn("目录不存在: {}", directory);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> structure = new HashMap<>();
|
|
|
+ structure.put("name", directory.getFileName().toString());
|
|
|
+ structure.put("type", Files.isDirectory(directory) ? "directory" : "file");
|
|
|
+ structure.put("path", directory.toString());
|
|
|
+
|
|
|
+ if (Files.isDirectory(directory)) {
|
|
|
+ List<Map<String, Object>> children = new ArrayList<>();
|
|
|
+
|
|
|
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory)) {
|
|
|
+ for (Path entry : stream) {
|
|
|
+ if (Files.isDirectory(entry)) {
|
|
|
+ children.add(getDirectoryStructure(entry));
|
|
|
+ } else {
|
|
|
+ children.add(getFileStructure(entry));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ structure.put("children", children);
|
|
|
+ } else {
|
|
|
+ // 如果是文件,添加文件大小
|
|
|
+ try {
|
|
|
+ structure.put("size", Files.size(directory));
|
|
|
+ } catch (IOException e) {
|
|
|
+ structure.put("size", 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return structure;
|
|
|
+
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.error("获取目录结构异常,目录: {}", directory, e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取文件结构
|
|
|
+ *
|
|
|
+ * @param file 文件路径
|
|
|
+ * @return 文件结构Map
|
|
|
+ */
|
|
|
+ public static Map<String, Object> getFileStructure(Path file) {
|
|
|
+ Map<String, Object> structure = new HashMap<>();
|
|
|
+ structure.put("name", file.getFileName().toString());
|
|
|
+ structure.put("type", "file");
|
|
|
+ structure.put("path", file.toString());
|
|
|
+
|
|
|
+ try {
|
|
|
+ structure.put("size", Files.size(file));
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.warn("获取文件大小失败: {}", file, e);
|
|
|
+ structure.put("size", 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ return structure;
|
|
|
+ }
|
|
|
+}
|