Skip to content

Commit ab370e6

Browse files
authored
Merge pull request #554 from kekingcn/kl
重构解压缩逻辑,fix #553
2 parents 42f0e07 + 421a276 commit ab370e6

File tree

2 files changed

+71
-66
lines changed

2 files changed

+71
-66
lines changed

server/src/main/java/cn/keking/service/CompressFileReader.java

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@
1212
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
1313
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
1414
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
15-
import org.apache.commons.io.IOUtils;
1615
import org.springframework.stereotype.Component;
17-
import org.springframework.util.ObjectUtils;
1816

19-
import java.io.*;
17+
import java.io.BufferedOutputStream;
18+
import java.io.FileOutputStream;
19+
import java.io.IOException;
20+
import java.io.OutputStream;
21+
import java.io.RandomAccessFile;
22+
import java.io.UnsupportedEncodingException;
2023
import java.nio.charset.StandardCharsets;
24+
import java.nio.file.Files;
25+
import java.nio.file.Path;
26+
import java.nio.file.Paths;
2127
import java.util.ArrayList;
2228
import java.util.List;
2329

@@ -37,73 +43,67 @@ public CompressFileReader(FileHandlerService fileHandlerService) {
3743
public String unRar(String filePath, String filePassword, String fileName, FileAttribute fileAttribute) throws Exception {
3844
List<String> imgUrls = new ArrayList<>();
3945
String baseUrl = BaseUrlFilter.getBaseUrl();
40-
String packagePath = "_"; //防止文件名重复 压缩包统一生成文件添加_符号
46+
String packagePath = "_";
4147
String folderName = filePath.replace(fileDir, ""); //修复压缩包 多重目录获取路径错误
42-
if (fileAttribute.isCompressFile()) { //压缩包文件 直接赋予路径 不予下载
43-
folderName = "_decompression" + folderName; //重新修改多重压缩包 生成文件路径
48+
if (fileAttribute.isCompressFile()) {
49+
folderName = "_decompression" + folderName;
4450
}
45-
RandomAccessFile randomAccessFile = null;
46-
IInArchive inArchive = null;
47-
try {
48-
randomAccessFile = new RandomAccessFile(filePath, "r");
49-
inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));
51+
52+
Path folderPath = Paths.get(fileDir, folderName + packagePath);
53+
Files.createDirectories(folderPath);
54+
55+
try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r");
56+
IInArchive inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile))) {
57+
5058
ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
51-
final String[] str = {null};
5259
for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
5360
if (!item.isFolder()) {
54-
ExtractOperationResult result;
55-
String finalFolderName = folderName;
56-
result = item.extractSlow(data -> {
57-
try {
58-
str[0] = RarUtils.getUtf8String(item.getPath());
59-
if (RarUtils.isMessyCode(str[0])) {
60-
str[0] = new String(item.getPath().getBytes(StandardCharsets.ISO_8859_1), "gbk");
61-
}
62-
str[0] = str[0].replace("\\", File.separator); //Linux 下路径错误
63-
String str1 = str[0].substring(0, str[0].lastIndexOf(File.separator) + 1);
64-
File file = new File(fileDir, finalFolderName + packagePath + File.separator + str1);
65-
if (!file.exists()) {
66-
file.mkdirs();
67-
}
68-
OutputStream out = new FileOutputStream(fileDir + finalFolderName + packagePath + File.separator + str[0], true);
69-
IOUtils.write(data, out);
70-
out.close();
71-
} catch (Exception e) {
72-
e.printStackTrace();
73-
return Integer.parseInt(null);
61+
final Path filePathInsideArchive = getFilePathInsideArchive(item, folderPath);
62+
ExtractOperationResult result = item.extractSlow(data -> {
63+
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(filePathInsideArchive.toFile(), true))) {
64+
out.write(data);
65+
} catch (IOException e) {
66+
throw new RuntimeException(e);
7467
}
7568
return data.length;
7669
}, filePassword);
77-
if (result == ExtractOperationResult.OK) {
78-
FileType type = FileType.typeFromUrl(str[0]);
79-
if (type.equals(FileType.PICTURE)) {
80-
imgUrls.add(baseUrl + folderName + packagePath + "/" + str[0].replace("\\", "/"));
81-
}
82-
fileHandlerService.putImgCache(fileName + packagePath, imgUrls);
83-
} else {
84-
return null;
70+
71+
if (result != ExtractOperationResult.OK) {
72+
throw new Exception("Failed to extract RAR file.");
73+
}
74+
75+
FileType type = FileType.typeFromUrl(filePathInsideArchive.toString());
76+
if (type.equals(FileType.PICTURE)) {
77+
imgUrls.add(baseUrl + folderPath.relativize(filePathInsideArchive).toString().replace("\\", "/"));
8578
}
8679
}
8780
}
88-
return folderName + packagePath;
81+
fileHandlerService.putImgCache(fileName + packagePath, imgUrls);
8982
} catch (Exception e) {
90-
throw new Exception(e);
91-
} finally {
92-
if (inArchive != null) {
93-
try {
94-
inArchive.close();
95-
} catch (SevenZipException e) {
96-
System.err.println("Error closing archive: " + e);
97-
}
98-
}
99-
if (randomAccessFile != null) {
100-
try {
101-
randomAccessFile.close();
102-
} catch (IOException e) {
103-
System.err.println("Error closing file: " + e);
104-
}
105-
}
83+
throw new Exception("Error processing RAR file: " + e.getMessage(), e);
84+
}
85+
return folderName + packagePath;
86+
}
87+
88+
private Path getFilePathInsideArchive(ISimpleInArchiveItem item, Path folderPath) throws SevenZipException, UnsupportedEncodingException {
89+
String insideFileName = RarUtils.getUtf8String(item.getPath());
90+
if (RarUtils.isMessyCode(insideFileName)) {
91+
insideFileName = new String(item.getPath().getBytes(StandardCharsets.ISO_8859_1), "gbk");
92+
}
93+
94+
// 正规化路径并验证是否安全
95+
Path normalizedPath = folderPath.resolve(insideFileName).normalize();
96+
if (!normalizedPath.startsWith(folderPath)) {
97+
throw new SecurityException("Unsafe path detected: " + insideFileName);
10698
}
99+
100+
try {
101+
Files.createDirectories(normalizedPath.getParent());
102+
} catch (IOException e) {
103+
throw new RuntimeException("Failed to create directory: " + normalizedPath.getParent(), e);
104+
}
105+
return normalizedPath;
107106
}
108107

108+
109109
}

server/src/main/java/cn/keking/service/impl/CompressFilePreviewImpl.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import cn.keking.utils.KkFileUtils;
1111
import org.apache.commons.lang3.exception.ExceptionUtils;
1212
import org.apache.poi.EncryptedDocumentException;
13+
import org.slf4j.Logger;
1314
import org.springframework.stereotype.Service;
1415
import org.springframework.ui.Model;
1516
import org.springframework.util.ObjectUtils;
@@ -28,6 +29,9 @@ public class CompressFilePreviewImpl implements FilePreview {
2829
private final CompressFileReader compressFileReader;
2930
private final OtherFilePreviewImpl otherFilePreview;
3031
private static final String Rar_PASSWORD_MSG = "password";
32+
private static final Logger logger = org.slf4j.LoggerFactory.getLogger(CompressFileReader.class);
33+
34+
3135
public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFileReader compressFileReader, OtherFilePreviewImpl otherFilePreview) {
3236
this.fileHandlerService = fileHandlerService;
3337
this.compressFileReader = compressFileReader;
@@ -36,20 +40,21 @@ public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFi
3640

3741
@Override
3842
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
39-
String fileName=fileAttribute.getName();
43+
String fileName = fileAttribute.getName();
4044
String filePassword = fileAttribute.getFilePassword();
41-
boolean forceUpdatedCache=fileAttribute.forceUpdatedCache();
45+
boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
4246
String fileTree = null;
4347
// 判断文件名是否存在(redis缓存读取)
44-
if (forceUpdatedCache || !StringUtils.hasText(fileHandlerService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
48+
if (forceUpdatedCache || !StringUtils.hasText(fileHandlerService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
4549
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
4650
if (response.isFailure()) {
4751
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
4852
}
4953
String filePath = response.getContent();
5054
try {
51-
fileTree = compressFileReader.unRar(filePath, filePassword,fileName, fileAttribute);
55+
fileTree = compressFileReader.unRar(filePath, filePassword, fileName, fileAttribute);
5256
} catch (Exception e) {
57+
logger.error("Error processing RAR file: " + e.getMessage(), e);
5358
Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
5459
for (Throwable throwable : throwableArray) {
5560
if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
@@ -69,14 +74,14 @@ public String filePreviewHandle(String url, Model model, FileAttribute fileAttri
6974
// 加入缓存
7075
fileHandlerService.addConvertedFile(fileName, fileTree);
7176
}
72-
}else {
73-
return otherFilePreview.notSupportedFile(model, fileAttribute, "压缩文件密码错误! 压缩文件损坏! 压缩文件类型不受支持!");
77+
} else {
78+
return otherFilePreview.notSupportedFile(model, fileAttribute, "该压缩包文件无法处理!");
7479
}
7580
} else {
7681
fileTree = fileHandlerService.getConvertedFile(fileName);
7782
}
78-
model.addAttribute("fileName", fileName);
79-
model.addAttribute("fileTree", fileTree);
80-
return COMPRESS_FILE_PREVIEW_PAGE;
83+
model.addAttribute("fileName", fileName);
84+
model.addAttribute("fileTree", fileTree);
85+
return COMPRESS_FILE_PREVIEW_PAGE;
8186
}
8287
}

0 commit comments

Comments
 (0)