重构解压缩逻辑,fix #553
This commit is contained in:
parent
42f0e079f0
commit
421a2760d5
|
@ -12,12 +12,18 @@ import net.sf.sevenzipjbinding.SevenZipException;
|
||||||
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
|
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
|
||||||
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
|
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
|
||||||
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.springframework.stereotype.Component;
|
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.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -37,73 +43,67 @@ public class CompressFileReader {
|
||||||
public String unRar(String filePath, String filePassword, String fileName, FileAttribute fileAttribute) throws Exception {
|
public String unRar(String filePath, String filePassword, String fileName, FileAttribute fileAttribute) throws Exception {
|
||||||
List<String> imgUrls = new ArrayList<>();
|
List<String> imgUrls = new ArrayList<>();
|
||||||
String baseUrl = BaseUrlFilter.getBaseUrl();
|
String baseUrl = BaseUrlFilter.getBaseUrl();
|
||||||
String packagePath = "_"; //防止文件名重复 压缩包统一生成文件添加_符号
|
String packagePath = "_";
|
||||||
String folderName = filePath.replace(fileDir, ""); //修复压缩包 多重目录获取路径错误
|
String folderName = filePath.replace(fileDir, ""); //修复压缩包 多重目录获取路径错误
|
||||||
if (fileAttribute.isCompressFile()) { //压缩包文件 直接赋予路径 不予下载
|
if (fileAttribute.isCompressFile()) {
|
||||||
folderName = "_decompression" + folderName; //重新修改多重压缩包 生成文件路径
|
folderName = "_decompression" + folderName;
|
||||||
}
|
}
|
||||||
RandomAccessFile randomAccessFile = null;
|
|
||||||
IInArchive inArchive = null;
|
Path folderPath = Paths.get(fileDir, folderName + packagePath);
|
||||||
try {
|
Files.createDirectories(folderPath);
|
||||||
randomAccessFile = new RandomAccessFile(filePath, "r");
|
|
||||||
inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));
|
try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r");
|
||||||
|
IInArchive inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile))) {
|
||||||
|
|
||||||
ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
|
ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
|
||||||
final String[] str = {null};
|
|
||||||
for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
|
for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
|
||||||
if (!item.isFolder()) {
|
if (!item.isFolder()) {
|
||||||
ExtractOperationResult result;
|
final Path filePathInsideArchive = getFilePathInsideArchive(item, folderPath);
|
||||||
String finalFolderName = folderName;
|
ExtractOperationResult result = item.extractSlow(data -> {
|
||||||
result = item.extractSlow(data -> {
|
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(filePathInsideArchive.toFile(), true))) {
|
||||||
try {
|
out.write(data);
|
||||||
str[0] = RarUtils.getUtf8String(item.getPath());
|
} catch (IOException e) {
|
||||||
if (RarUtils.isMessyCode(str[0])) {
|
throw new RuntimeException(e);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
return data.length;
|
return data.length;
|
||||||
}, filePassword);
|
}, filePassword);
|
||||||
if (result == ExtractOperationResult.OK) {
|
|
||||||
FileType type = FileType.typeFromUrl(str[0]);
|
if (result != ExtractOperationResult.OK) {
|
||||||
if (type.equals(FileType.PICTURE)) {
|
throw new Exception("Failed to extract RAR file.");
|
||||||
imgUrls.add(baseUrl + folderName + packagePath + "/" + str[0].replace("\\", "/"));
|
|
||||||
}
|
|
||||||
fileHandlerService.putImgCache(fileName + packagePath, imgUrls);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return folderName + packagePath;
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileType type = FileType.typeFromUrl(filePathInsideArchive.toString());
|
||||||
|
if (type.equals(FileType.PICTURE)) {
|
||||||
|
imgUrls.add(baseUrl + folderPath.relativize(filePathInsideArchive).toString().replace("\\", "/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileHandlerService.putImgCache(fileName + packagePath, imgUrls);
|
||||||
|
} catch (Exception 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import cn.keking.service.CompressFileReader;
|
||||||
import cn.keking.utils.KkFileUtils;
|
import cn.keking.utils.KkFileUtils;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import org.apache.poi.EncryptedDocumentException;
|
import org.apache.poi.EncryptedDocumentException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
@ -28,6 +29,9 @@ public class CompressFilePreviewImpl implements FilePreview {
|
||||||
private final CompressFileReader compressFileReader;
|
private final CompressFileReader compressFileReader;
|
||||||
private final OtherFilePreviewImpl otherFilePreview;
|
private final OtherFilePreviewImpl otherFilePreview;
|
||||||
private static final String Rar_PASSWORD_MSG = "password";
|
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) {
|
public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFileReader compressFileReader, OtherFilePreviewImpl otherFilePreview) {
|
||||||
this.fileHandlerService = fileHandlerService;
|
this.fileHandlerService = fileHandlerService;
|
||||||
this.compressFileReader = compressFileReader;
|
this.compressFileReader = compressFileReader;
|
||||||
|
@ -36,9 +40,9 @@ public class CompressFilePreviewImpl implements FilePreview {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
|
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
|
||||||
String fileName=fileAttribute.getName();
|
String fileName = fileAttribute.getName();
|
||||||
String filePassword = fileAttribute.getFilePassword();
|
String filePassword = fileAttribute.getFilePassword();
|
||||||
boolean forceUpdatedCache=fileAttribute.forceUpdatedCache();
|
boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
|
||||||
String fileTree = null;
|
String fileTree = null;
|
||||||
// 判断文件名是否存在(redis缓存读取)
|
// 判断文件名是否存在(redis缓存读取)
|
||||||
if (forceUpdatedCache || !StringUtils.hasText(fileHandlerService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
|
if (forceUpdatedCache || !StringUtils.hasText(fileHandlerService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
|
||||||
|
@ -48,8 +52,9 @@ public class CompressFilePreviewImpl implements FilePreview {
|
||||||
}
|
}
|
||||||
String filePath = response.getContent();
|
String filePath = response.getContent();
|
||||||
try {
|
try {
|
||||||
fileTree = compressFileReader.unRar(filePath, filePassword,fileName, fileAttribute);
|
fileTree = compressFileReader.unRar(filePath, filePassword, fileName, fileAttribute);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
logger.error("Error processing RAR file: " + e.getMessage(), e);
|
||||||
Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
|
Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
|
||||||
for (Throwable throwable : throwableArray) {
|
for (Throwable throwable : throwableArray) {
|
||||||
if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
|
if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
|
||||||
|
@ -69,8 +74,8 @@ public class CompressFilePreviewImpl implements FilePreview {
|
||||||
// 加入缓存
|
// 加入缓存
|
||||||
fileHandlerService.addConvertedFile(fileName, fileTree);
|
fileHandlerService.addConvertedFile(fileName, fileTree);
|
||||||
}
|
}
|
||||||
}else {
|
} else {
|
||||||
return otherFilePreview.notSupportedFile(model, fileAttribute, "压缩文件密码错误! 压缩文件损坏! 压缩文件类型不受支持!");
|
return otherFilePreview.notSupportedFile(model, fileAttribute, "该压缩包文件无法处理!");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fileTree = fileHandlerService.getConvertedFile(fileName);
|
fileTree = fileHandlerService.getConvertedFile(fileName);
|
||||||
|
|
Loading…
Reference in New Issue