Browse Source

Merge pull request #554 from kekingcn/kl

重构解压缩逻辑,fix  #553
陈精华 1 year ago
parent
commit
ab370e66a5

+ 57 - 57
server/src/main/java/cn/keking/service/CompressFileReader.java

@@ -12,12 +12,18 @@ import net.sf.sevenzipjbinding.SevenZipException;
 import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
 import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
 import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
-import org.apache.commons.io.IOUtils;
 import org.springframework.stereotype.Component;
-import org.springframework.util.ObjectUtils;
 
-import java.io.*;
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -37,73 +43,67 @@ public class CompressFileReader {
     public String unRar(String filePath, String filePassword, String fileName, FileAttribute fileAttribute) throws Exception {
         List<String> imgUrls = new ArrayList<>();
         String baseUrl = BaseUrlFilter.getBaseUrl();
-        String packagePath = "_"; //防止文件名重复 压缩包统一生成文件添加_符号
+        String packagePath = "_";
         String folderName = filePath.replace(fileDir, ""); //修复压缩包 多重目录获取路径错误
-        if (fileAttribute.isCompressFile()) { //压缩包文件 直接赋予路径 不予下载
-            folderName = "_decompression" + folderName;  //重新修改多重压缩包 生成文件路径
+        if (fileAttribute.isCompressFile()) {
+            folderName = "_decompression" + folderName;
         }
-        RandomAccessFile randomAccessFile = null;
-        IInArchive inArchive = null;
-        try {
-            randomAccessFile = new RandomAccessFile(filePath, "r");
-            inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));
+
+        Path folderPath = Paths.get(fileDir, folderName + packagePath);
+        Files.createDirectories(folderPath);
+
+        try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r");
+             IInArchive inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile))) {
+
             ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
-            final String[] str = {null};
             for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                 if (!item.isFolder()) {
-                    ExtractOperationResult result;
-                    String finalFolderName = folderName;
-                    result = item.extractSlow(data -> {
-                        try {
-                            str[0] = RarUtils.getUtf8String(item.getPath());
-                            if (RarUtils.isMessyCode(str[0])) {
-                                str[0] = new String(item.getPath().getBytes(StandardCharsets.ISO_8859_1), "gbk");
-                            }
-                            str[0] = str[0].replace("\\", File.separator); //Linux 下路径错误
-                            String str1 = str[0].substring(0, str[0].lastIndexOf(File.separator) + 1);
-                            File file = new File(fileDir, finalFolderName + packagePath + File.separator + str1);
-                            if (!file.exists()) {
-                                file.mkdirs();
-                            }
-                            OutputStream out = new FileOutputStream(fileDir + finalFolderName + packagePath + File.separator + str[0], true);
-                            IOUtils.write(data, out);
-                            out.close();
-                        } catch (Exception e) {
-                            e.printStackTrace();
-                            return Integer.parseInt(null);
+                    final Path filePathInsideArchive = getFilePathInsideArchive(item, folderPath);
+                    ExtractOperationResult result = item.extractSlow(data -> {
+                        try (OutputStream out = new BufferedOutputStream(new FileOutputStream(filePathInsideArchive.toFile(), true))) {
+                            out.write(data);
+                        } catch (IOException e) {
+                            throw new RuntimeException(e);
                         }
                         return data.length;
                     }, filePassword);
-                    if (result == ExtractOperationResult.OK) {
-                        FileType type = FileType.typeFromUrl(str[0]);
-                        if (type.equals(FileType.PICTURE)) {
-                            imgUrls.add(baseUrl + folderName + packagePath + "/" + str[0].replace("\\", "/"));
-                        }
-                        fileHandlerService.putImgCache(fileName + packagePath, imgUrls);
-                    } else {
-                        return null;
+
+                    if (result != ExtractOperationResult.OK) {
+                        throw new Exception("Failed to extract RAR file.");
+                    }
+
+                    FileType type = FileType.typeFromUrl(filePathInsideArchive.toString());
+                    if (type.equals(FileType.PICTURE)) {
+                        imgUrls.add(baseUrl + folderPath.relativize(filePathInsideArchive).toString().replace("\\", "/"));
                     }
                 }
             }
-            return folderName + packagePath;
+            fileHandlerService.putImgCache(fileName + packagePath, imgUrls);
         } catch (Exception e) {
-            throw new Exception(e);
-        } finally {
-            if (inArchive != null) {
-                try {
-                    inArchive.close();
-                } catch (SevenZipException e) {
-                    System.err.println("Error closing archive: " + e);
-                }
-            }
-            if (randomAccessFile != null) {
-                try {
-                    randomAccessFile.close();
-                } catch (IOException e) {
-                    System.err.println("Error closing file: " + e);
-                }
-            }
+            throw new Exception("Error processing RAR file: " + e.getMessage(), e);
+        }
+        return folderName + packagePath;
+    }
+
+    private Path getFilePathInsideArchive(ISimpleInArchiveItem item, Path folderPath) throws SevenZipException, UnsupportedEncodingException {
+        String insideFileName = RarUtils.getUtf8String(item.getPath());
+        if (RarUtils.isMessyCode(insideFileName)) {
+            insideFileName = new String(item.getPath().getBytes(StandardCharsets.ISO_8859_1), "gbk");
+        }
+
+        // 正规化路径并验证是否安全
+        Path normalizedPath = folderPath.resolve(insideFileName).normalize();
+        if (!normalizedPath.startsWith(folderPath)) {
+            throw new SecurityException("Unsafe path detected: " + insideFileName);
         }
+
+        try {
+            Files.createDirectories(normalizedPath.getParent());
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to create directory: " + normalizedPath.getParent(), e);
+        }
+        return normalizedPath;
     }
 
+
 }

+ 14 - 9
server/src/main/java/cn/keking/service/impl/CompressFilePreviewImpl.java

@@ -10,6 +10,7 @@ import cn.keking.service.CompressFileReader;
 import cn.keking.utils.KkFileUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.poi.EncryptedDocumentException;
+import org.slf4j.Logger;
 import org.springframework.stereotype.Service;
 import org.springframework.ui.Model;
 import org.springframework.util.ObjectUtils;
@@ -28,6 +29,9 @@ public class CompressFilePreviewImpl implements FilePreview {
     private final CompressFileReader compressFileReader;
     private final OtherFilePreviewImpl otherFilePreview;
     private static final String Rar_PASSWORD_MSG = "password";
+    private static final Logger logger = org.slf4j.LoggerFactory.getLogger(CompressFileReader.class);
+
+
     public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFileReader compressFileReader, OtherFilePreviewImpl otherFilePreview) {
         this.fileHandlerService = fileHandlerService;
         this.compressFileReader = compressFileReader;
@@ -36,20 +40,21 @@ public class CompressFilePreviewImpl implements FilePreview {
 
     @Override
     public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
-        String fileName=fileAttribute.getName();
+        String fileName = fileAttribute.getName();
         String filePassword = fileAttribute.getFilePassword();
-        boolean forceUpdatedCache=fileAttribute.forceUpdatedCache();
+        boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
         String fileTree = null;
         // 判断文件名是否存在(redis缓存读取)
-        if (forceUpdatedCache || !StringUtils.hasText(fileHandlerService.getConvertedFile(fileName))  || !ConfigConstants.isCacheEnabled()) {
+        if (forceUpdatedCache || !StringUtils.hasText(fileHandlerService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
             ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
             if (response.isFailure()) {
                 return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
             }
             String filePath = response.getContent();
             try {
-                fileTree = compressFileReader.unRar(filePath, filePassword,fileName, fileAttribute);
+                fileTree = compressFileReader.unRar(filePath, filePassword, fileName, fileAttribute);
             } catch (Exception e) {
+                logger.error("Error processing RAR file: " + e.getMessage(), e);
                 Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
                 for (Throwable throwable : throwableArray) {
                     if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
@@ -69,14 +74,14 @@ public class CompressFilePreviewImpl implements FilePreview {
                     // 加入缓存
                     fileHandlerService.addConvertedFile(fileName, fileTree);
                 }
-            }else {
-                return otherFilePreview.notSupportedFile(model, fileAttribute, "压缩文件密码错误! 压缩文件损坏!  压缩文件类型不受支持!");
+            } else {
+                return otherFilePreview.notSupportedFile(model, fileAttribute, "该压缩包文件无法处理!");
             }
         } else {
             fileTree = fileHandlerService.getConvertedFile(fileName);
         }
-            model.addAttribute("fileName", fileName);
-            model.addAttribute("fileTree", fileTree);
-            return COMPRESS_FILE_PREVIEW_PAGE;
+        model.addAttribute("fileName", fileName);
+        model.addAttribute("fileTree", fileTree);
+        return COMPRESS_FILE_PREVIEW_PAGE;
     }
 }