提交代码
This commit is contained in:
parent
62cf72359a
commit
b08961f2d3
@ -116,7 +116,7 @@ public class TsFilesController {
|
||||
@ApiOperation("新增试验数据管理文件夹")
|
||||
@ResponseBody
|
||||
@PreAuthorize("@el.check('add:tsFiles')")
|
||||
public ResponseResult addTsFile(@RequestBody TsFiles tsFiles) {
|
||||
public ResponseResult addTsFile(@RequestBody TsFiles tsFiles) throws IOException {
|
||||
//对象不能为空
|
||||
if (ObjUtil.isEmpty(tsFiles)) {
|
||||
return ResponseResult.error("参数为空");
|
||||
@ -461,4 +461,6 @@ public class TsFilesController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ public interface ITsFilesService extends IService<TsFiles> {
|
||||
* TsFiles 文档内容
|
||||
* 返回值说明: com.yfd.platform.config.ResponseResult 返回新增成功或者失败
|
||||
***********************************/
|
||||
ResponseResult addTsFile(TsFiles tsFiles);
|
||||
ResponseResult addTsFile(TsFiles tsFiles) throws IOException;
|
||||
|
||||
/**********************************
|
||||
* 用途说明: 查询实验数据管理文件夹
|
||||
|
@ -5,6 +5,7 @@ import cn.hutool.core.collection.CollUtil;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.SimpleDateFormat;
|
||||
@ -62,6 +63,7 @@ import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -75,10 +77,7 @@ import javax.xml.crypto.Data;
|
||||
import java.io.*;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.*;
|
||||
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
@ -472,8 +471,41 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
***********************************/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)// 添加事务注解,遇到异常时回滚
|
||||
public ResponseResult addTsFile(TsFiles tsFiles) {
|
||||
public ResponseResult addTsFile(TsFiles tsFiles) throws IOException {
|
||||
|
||||
if (tsFiles.getIsFile().equals("FILE")) {
|
||||
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
|
||||
String basePath = config.getValue() + tsFiles.getWorkPath();
|
||||
// 拼接完整文件路径
|
||||
|
||||
// 拼接完整的文件路径
|
||||
Path filePath = Paths.get(basePath, tsFiles.getFileName() + ".txt");
|
||||
|
||||
// 确保目录存在,如果不存在则创建目录
|
||||
Files.createDirectories(filePath.getParent());
|
||||
|
||||
// 使用 Files.write() 写入字符串内容
|
||||
Files.write(filePath, "默认内容".getBytes(StandardCharsets.UTF_8));
|
||||
tsFiles.setFileName(tsFiles.getFileName() + ".txt");
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
|
||||
LoginUser loginuser = (LoginUser) authentication.getPrincipal();
|
||||
// 设置当前时间
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
// 转换为 Timestamp
|
||||
Timestamp currentTime = Timestamp.valueOf(now);
|
||||
tsFiles.setUploadTime(currentTime);
|
||||
tsFiles.setUploader(loginuser.getUsername());
|
||||
tsFiles.setFileSize("0");
|
||||
int valueAdded = tsFilesMapper.insert(tsFiles);
|
||||
if (valueAdded == 1) {
|
||||
LOGGER.info("文件创建成功");
|
||||
return ResponseResult.success();
|
||||
} else {
|
||||
LOGGER.error("文件创建失败");
|
||||
return ResponseResult.error();
|
||||
}
|
||||
} else {
|
||||
// 校验文件名是否包含非法字符
|
||||
String fileName = tsFiles.getFileName();
|
||||
if (containsInvalidCharacters(fileName)) {
|
||||
@ -547,6 +579,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
return ResponseResult.error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 校验文件名是否包含非法字符
|
||||
private boolean containsInvalidCharacters(String fileName) {
|
||||
@ -1018,13 +1051,11 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
String zipFileName = compressedName + "." + compressedFormat;
|
||||
|
||||
// 使用 try-with-resources 确保 zipOut 在使用后关闭
|
||||
try (ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream(new FileOutputStream(zipFilePath.toFile()))) {
|
||||
// 设置压缩级别,可以选择调整
|
||||
zipOut.setLevel(ZipArchiveOutputStream.STORED); // 或者使用其他的压缩级别
|
||||
// zipOut.setLevel(ZipArchiveOutputStream.STORED); // 或者使用其他的压缩级别
|
||||
// 调用压缩方法
|
||||
Boolean value = compressToSameDirectory(sourceDirs, compressedFormat);
|
||||
if (value) {
|
||||
zipOut.finish(); // 确保所有数据都已写入
|
||||
//表结构增加 nodeId, String TaskId
|
||||
TsFiles tsFiles = new TsFiles();
|
||||
tsFiles.setTaskId(filesList.get(0).getTaskId());
|
||||
@ -1098,100 +1129,26 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
} else {
|
||||
returnResult = "压缩失败";
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return returnResult;
|
||||
}
|
||||
|
||||
|
||||
// ================== 核心方法改造 ==================
|
||||
|
||||
/**
|
||||
* 多路径压缩到各自同级目录
|
||||
* @param sourcePaths 要压缩的路径集合
|
||||
* @param compressionFormat 前端传入的压缩格式字符串
|
||||
* @return 压缩成功返回true,失败返回false
|
||||
*/
|
||||
public boolean compressToSameDirectory(List<Path> sourcePaths, String compressionFormat) {
|
||||
|
||||
private Boolean compressToSameDirectory(List<Path> sourcePaths, String compressedFormat) {
|
||||
try {
|
||||
// 转换压缩格式
|
||||
CompressionFormat format = CompressionFormat.fromString(compressionFormat);
|
||||
|
||||
// 遍历所有路径进行压缩
|
||||
for (Path sourcePath : sourcePaths) {
|
||||
// 验证路径有效性
|
||||
validatePath(sourcePath);
|
||||
|
||||
// 生成目标路径
|
||||
Path targetPath = generateTargetPath(sourcePath, format);
|
||||
|
||||
// 执行压缩
|
||||
if (!compressSinglePath(sourcePath, targetPath, format)) {
|
||||
return false; // 任一压缩失败则终止
|
||||
switch (compressedFormat.toUpperCase()) {
|
||||
case "ZIP":
|
||||
return compressToZip(sourcePaths);
|
||||
case "TAR.GZ":
|
||||
return compressToTarGz(sourcePaths);
|
||||
// case "7Z":
|
||||
// return compressTo7z(sourcePaths);
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的压缩格式: " + compressedFormat);
|
||||
}
|
||||
}
|
||||
return true; // 全部压缩成功
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println("格式错误: " + e.getMessage());
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
System.err.println("压缩失败: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ================== 核心私有方法 ==================
|
||||
|
||||
/**
|
||||
* 验证路径有效性
|
||||
* @param path 待验证路径
|
||||
* @throws IOException 路径无效时抛出
|
||||
*/
|
||||
private void validatePath(Path path) throws IOException {
|
||||
if (!Files.exists(path)) {
|
||||
throw new FileNotFoundException("路径不存在: " + path);
|
||||
}
|
||||
if (!Files.isReadable(path)) {
|
||||
throw new AccessDeniedException("无读取权限: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成目标压缩文件路径
|
||||
* @param sourcePath 源路径
|
||||
* @param format 压缩格式
|
||||
* @return 目标路径(同级目录+格式后缀)
|
||||
*/
|
||||
private Path generateTargetPath(Path sourcePath, CompressionFormat format) {
|
||||
// 获取无扩展名的文件名
|
||||
String baseName = FilenameUtils.removeExtension(sourcePath.getFileName().toString());
|
||||
// 添加压缩后缀
|
||||
String fileName = baseName + "." + format.getPrimaryExtension();
|
||||
return sourcePath.resolveSibling(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单路径压缩核心方法
|
||||
* @return 压缩成功返回true,失败返回false
|
||||
*/
|
||||
private boolean compressSinglePath(Path sourcePath, Path targetPath, CompressionFormat format) {
|
||||
try {
|
||||
switch (format) {
|
||||
case ZIP:
|
||||
compressToZip(sourcePath, targetPath);
|
||||
break;
|
||||
case TAR_GZ:
|
||||
compressToTarGz(sourcePath, targetPath);
|
||||
break;
|
||||
case SEVEN_ZIP:
|
||||
compressTo7z(sourcePath, targetPath);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
System.err.println("压缩失败: " + sourcePath);
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
@ -1199,222 +1156,252 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
|
||||
// ================== ZIP格式压缩实现 ==================
|
||||
|
||||
private void compressToZip(Path source, Path target) throws IOException {
|
||||
try (ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream(Files.newOutputStream(target))) {
|
||||
Set<Path> processedPaths = new HashSet<>();
|
||||
processZipEntry(source, zipOut, "", processedPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩多个文件/目录到单个ZIP
|
||||
*/
|
||||
public boolean compressToZip(List<Path> sourcePaths) {
|
||||
if (sourcePaths.isEmpty()) {
|
||||
LOGGER.warn("源路径列表为空,无法压缩");
|
||||
return false;
|
||||
}
|
||||
|
||||
private void processZipEntry(Path currentPath,
|
||||
ZipArchiveOutputStream zipOut,
|
||||
String parentEntry,
|
||||
Set<Path> processedPaths) throws IOException {
|
||||
// 获取相对于压缩根的路径
|
||||
Path relativePath = getRelativePath(currentPath);
|
||||
Path zipPath = generateZipPath(sourcePaths.get(0));
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipPath))) {
|
||||
zipOut.setLevel(Deflater.DEFAULT_COMPRESSION);
|
||||
|
||||
// 构建条目名称(关键修改)
|
||||
String entryName = buildZipEntryName(relativePath, parentEntry);
|
||||
|
||||
Path realPath = currentPath.toRealPath();
|
||||
|
||||
if (Files.isDirectory(currentPath)) {
|
||||
if (processedPaths.contains(realPath)) return;
|
||||
processedPaths.add(realPath);
|
||||
}
|
||||
|
||||
if (Files.isDirectory(currentPath)) {
|
||||
handleZipDirectory(zipOut, entryName);
|
||||
processZipChildren(currentPath, zipOut, entryName, processedPaths);
|
||||
for (Path sourcePath : sourcePaths) {
|
||||
if (Files.isDirectory(sourcePath)) {
|
||||
addDirectoryToZip(sourcePath, zipOut, sourcePath.getFileName().toString());
|
||||
} else {
|
||||
addFileToZip(currentPath, zipOut, entryName);
|
||||
addFileToZip(sourcePath, zipOut, "");
|
||||
}
|
||||
}
|
||||
|
||||
// ================== TAR.GZ格式压缩实现 ==================
|
||||
|
||||
private void compressToTarGz(Path source, Path target) throws IOException {
|
||||
try (TarArchiveOutputStream tarOut = new TarArchiveOutputStream(
|
||||
new GzipCompressorOutputStream(Files.newOutputStream(target)))) {
|
||||
Set<Path> processedPaths = new HashSet<>();
|
||||
processTarEntry(source, tarOut, "", processedPaths);
|
||||
zipOut.finish();
|
||||
return validateZip(zipPath);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("压缩失败", e);
|
||||
deleteQuietly(zipPath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void addDirectoryToZip(Path dir, ZipOutputStream zipOut, String baseDir) throws IOException {
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
String entryName = baseDir + "/" + dir.relativize(dir).toString().replace("\\", "/") + "/";
|
||||
ZipEntry entry = new ZipEntry(entryName);
|
||||
zipOut.putNextEntry(entry);
|
||||
zipOut.closeEntry();
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
String entryName = baseDir + "/" + dir.relativize(file).toString().replace("\\", "/");
|
||||
addFileEntry(file, entryName, zipOut);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addFileToZip(Path file, ZipOutputStream zipOut, String baseDir) throws IOException {
|
||||
String entryName = baseDir + file.getFileName().toString();
|
||||
addFileEntry(file, entryName, zipOut);
|
||||
}
|
||||
|
||||
private void addFileEntry(Path file, String entryName, ZipOutputStream zipOut) throws IOException {
|
||||
LOGGER.debug("添加文件条目: {} (大小: {} 字节)", entryName, Files.size(file));
|
||||
ZipEntry entry = new ZipEntry(entryName);
|
||||
entry.setSize(Files.size(file));
|
||||
entry.setTime(Files.getLastModifiedTime(file).toMillis());
|
||||
zipOut.putNextEntry(entry);
|
||||
|
||||
try (InputStream input = Files.newInputStream(file)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
while ((bytesRead = input.read(buffer)) != -1) {
|
||||
zipOut.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
zipOut.closeEntry();
|
||||
}
|
||||
|
||||
private Path generateZipPath(Path sourcePath) {
|
||||
String baseName = sourcePath.getFileName().toString().replaceFirst("[.][^.]+$", "");
|
||||
return sourcePath.resolveSibling(baseName + ".zip");
|
||||
}
|
||||
|
||||
private boolean validateZip(Path zipPath) {
|
||||
try (ZipFile zipFile = new ZipFile(zipPath.toFile())) {
|
||||
Enumeration<? extends ZipEntry> entries = zipFile.entries(); // 获取所有条目
|
||||
if (!entries.hasMoreElements()) {
|
||||
LOGGER.warn("ZIP 文件为空: {}", zipPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry entry = entries.nextElement();
|
||||
try (InputStream is = zipFile.getInputStream(entry)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
while (is.read(buffer) != -1) {} // 读取条目内容
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("ZIP 文件验证成功: {}", zipPath);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("ZIP 文件验证失败: {}", zipPath, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteQuietly(Path path) {
|
||||
try {
|
||||
Files.deleteIfExists(path);
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相对于压缩根的路径
|
||||
* 生成压缩文件路径(去除源文件扩展名)
|
||||
*/
|
||||
private Path getRelativePath(Path absolutePath) {
|
||||
// 示例:如果压缩根是 /data/1.txt,则返回空路径
|
||||
// 如果是压缩目录 /data/333,则返回相对路径
|
||||
// 需要根据实际压缩根路径调整实现
|
||||
return absolutePath.getFileName();
|
||||
private Path getCompressedFilePath(Path sourcePath, String extension) {
|
||||
String fileName = sourcePath.getFileName().toString();
|
||||
String baseName = fileName.contains(".")
|
||||
? fileName.substring(0, fileName.lastIndexOf('.'))
|
||||
: fileName;
|
||||
return sourcePath.resolveSibling(baseName + "." + extension);
|
||||
}
|
||||
|
||||
private void processTarEntry(Path currentPath,
|
||||
TarArchiveOutputStream tarOut,
|
||||
String parentEntry,
|
||||
Set<Path> processedPaths) throws IOException {
|
||||
Path realPath = currentPath.toRealPath();
|
||||
|
||||
if (Files.isDirectory(currentPath)) {
|
||||
if (processedPaths.contains(realPath)) return;
|
||||
processedPaths.add(realPath);
|
||||
}
|
||||
|
||||
String entryName = buildTarEntryName(currentPath, parentEntry);
|
||||
TarArchiveEntry entry = new TarArchiveEntry(currentPath.toFile(), entryName);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ================== TAR.GZ格式压缩实现 ==================
|
||||
private boolean compressToTarGz(List<Path> sourcePaths) throws IOException {
|
||||
boolean success = true;
|
||||
for (Path sourcePath : sourcePaths) {
|
||||
Path tarGzPath = getCompressedFilePath(sourcePath, "tar.gz");
|
||||
try (OutputStream fos = Files.newOutputStream(tarGzPath);
|
||||
GzipCompressorOutputStream gzos = new GzipCompressorOutputStream(fos);
|
||||
TarArchiveOutputStream tarOut = new TarArchiveOutputStream(gzos)) {
|
||||
|
||||
tarOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
|
||||
|
||||
processEntries(sourcePath, (entryName, isDir) -> {
|
||||
Path filePath = sourcePath.resolve(entryName);
|
||||
TarArchiveEntry entry = new TarArchiveEntry(filePath.toFile(), entryName);
|
||||
entry.setSize(isDir ? 0 : Files.size(filePath));
|
||||
tarOut.putArchiveEntry(entry);
|
||||
tarOut.closeArchiveEntry();
|
||||
|
||||
if (Files.isDirectory(currentPath)) {
|
||||
try (DirectoryStream<Path> children = Files.newDirectoryStream(currentPath)) {
|
||||
for (Path child : children) {
|
||||
processTarEntry(child, tarOut, entryName, processedPaths);
|
||||
if (!isDir) {
|
||||
try (InputStream input = Files.newInputStream(filePath)) {
|
||||
IOUtils.copy(input, tarOut);
|
||||
}
|
||||
}
|
||||
|
||||
tarOut.closeArchiveEntry();
|
||||
});
|
||||
|
||||
tarOut.finish();
|
||||
} catch (Exception e) {
|
||||
success = false;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// ================== 7Z格式压缩实现 ==================
|
||||
|
||||
private void compressTo7z(Path source, Path target) throws IOException {
|
||||
try (SevenZOutputFile sevenZOut = new SevenZOutputFile(target.toFile())) {
|
||||
Set<Path> processedPaths = new HashSet<>();
|
||||
process7zEntry(source, sevenZOut, "", processedPaths);
|
||||
}
|
||||
}
|
||||
|
||||
private void process7zEntry(Path currentPath,
|
||||
SevenZOutputFile sevenZOut,
|
||||
String parentEntry,
|
||||
Set<Path> processedPaths) throws IOException {
|
||||
Path realPath = currentPath.toRealPath();
|
||||
|
||||
if (Files.isDirectory(currentPath)) {
|
||||
if (processedPaths.contains(realPath)) return;
|
||||
processedPaths.add(realPath);
|
||||
}
|
||||
|
||||
String entryName = build7zEntryName(currentPath, parentEntry);
|
||||
SevenZArchiveEntry entry = sevenZOut.createArchiveEntry(currentPath.toFile(), entryName);
|
||||
sevenZOut.putArchiveEntry(entry);
|
||||
sevenZOut.closeArchiveEntry();
|
||||
|
||||
if (Files.isDirectory(currentPath)) {
|
||||
try (DirectoryStream<Path> children = Files.newDirectoryStream(currentPath)) {
|
||||
for (Path child : children) {
|
||||
process7zEntry(child, sevenZOut, entryName, processedPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// private boolean compressTo7z(List<Path> sourcePaths) throws IOException {
|
||||
// boolean success = true;
|
||||
// for (Path sourcePath : sourcePaths) {
|
||||
// Path sevenZPath = getCompressedFilePath(sourcePath, "7z");
|
||||
// try (SevenZOutputFile sevenZOutput = new SevenZOutputFile(sevenZPath.toFile())) {
|
||||
//
|
||||
// processEntries(sourcePath, (entryName, isDir) -> {
|
||||
// SevenZArchiveEntry entry = sevenZOutput.createEntry();
|
||||
// entry.setName(entryName);
|
||||
// entry.setDirectory(isDir);
|
||||
//
|
||||
// if (!isDir) {
|
||||
// Path filePath = sourcePath.resolve(entryName);
|
||||
// entry.setSize(Files.size(filePath));
|
||||
// sevenZOutput.putArchiveEntry(entry);
|
||||
//
|
||||
// try (InputStream input = Files.newInputStream(filePath)) {
|
||||
// byte[] buffer = new byte[8192];
|
||||
// int len;
|
||||
// while ((len = input.read(buffer)) != -1) {
|
||||
// sevenZOutput.write(buffer, 0, len);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// sevenZOutput.closeArchiveEntry();
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// success = false;
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// return success;
|
||||
// }
|
||||
|
||||
// ================== 通用工具方法 ==================
|
||||
|
||||
private String buildZipEntryName(Path path, String parentEntry) {
|
||||
// 仅保留文件名部分
|
||||
String fileName = path.getFileName().toString();
|
||||
|
||||
// 根条目处理
|
||||
if (parentEntry.isEmpty()) {
|
||||
return Files.isDirectory(path) ? fileName + "/" : fileName;
|
||||
private interface EntryProcessor {
|
||||
void process(String entryName, boolean isDir) throws IOException;
|
||||
}
|
||||
|
||||
// 子条目处理
|
||||
return parentEntry + (parentEntry.endsWith("/") ? "" : "/") + fileName
|
||||
+ (Files.isDirectory(path) ? "/" : "");
|
||||
private void processEntries(Path rootPath, EntryProcessor processor) throws IOException {
|
||||
Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
String entryName = rootPath.relativize(dir).toString().replace("\\", "/");
|
||||
if (!entryName.isEmpty()) {
|
||||
processor.process(entryName + "/", true);
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
private String buildTarEntryName(Path path, String parentEntry) {
|
||||
return parentEntry.isEmpty() ?
|
||||
path.getFileName().toString() :
|
||||
parentEntry + "/" + path.getFileName();
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
String entryName = rootPath.relativize(file).toString().replace("\\", "/");
|
||||
processor.process(entryName, false);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String build7zEntryName(Path path, String parentEntry) {
|
||||
return buildTarEntryName(path, parentEntry);
|
||||
}
|
||||
|
||||
private void handleZipDirectory(ZipArchiveOutputStream zipOut, String entryName)
|
||||
throws IOException {
|
||||
// ================== ZIP条目添加方法 ==================
|
||||
private void addZipDirectoryEntry(ZipArchiveOutputStream zipOut, String entryName) throws IOException {
|
||||
ZipArchiveEntry entry = new ZipArchiveEntry(entryName);
|
||||
entry.setMethod(ZipEntry.STORED);
|
||||
entry.setSize(0);
|
||||
entry.setCrc(0);
|
||||
zipOut.putArchiveEntry(entry);
|
||||
zipOut.closeArchiveEntry();
|
||||
}
|
||||
|
||||
private void processZipChildren(Path dir,
|
||||
ZipArchiveOutputStream zipOut,
|
||||
String parentEntry,
|
||||
Set<Path> processedPaths) throws IOException {
|
||||
try (DirectoryStream<Path> children = Files.newDirectoryStream(dir)) {
|
||||
for (Path child : children) {
|
||||
processZipEntry(child, zipOut, parentEntry, processedPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addFileToZip(Path file, ZipArchiveOutputStream zipOut, String entryName)
|
||||
throws IOException {
|
||||
ZipArchiveEntry entry = new ZipArchiveEntry(entryName);
|
||||
entry.setMethod(ZipEntry.DEFLATED);
|
||||
entry.setSize(Files.size(file));
|
||||
entry.setTime(Files.getLastModifiedTime(file).toMillis());
|
||||
private void addZipFileEntry(ZipArchiveOutputStream zipOut, Path filePath, String entryName) throws IOException {
|
||||
ZipArchiveEntry entry = new ZipArchiveEntry(filePath.toFile(), entryName);
|
||||
entry.setSize(Files.size(filePath));
|
||||
entry.setTime(Files.getLastModifiedTime(filePath).toMillis());
|
||||
|
||||
zipOut.putArchiveEntry(entry);
|
||||
try (InputStream input = Files.newInputStream(file)) {
|
||||
try (InputStream input = Files.newInputStream(filePath)) {
|
||||
IOUtils.copy(input, zipOut);
|
||||
}
|
||||
zipOut.closeArchiveEntry();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************************解压缩*******************************************/
|
||||
|
||||
/**********************************
|
||||
@ -1442,8 +1429,12 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
// // 4. 构建解压目标路径
|
||||
Path destRoot;
|
||||
if (hasFolder) {
|
||||
// 动态去掉 .zip 后缀(仅用于路径构建)
|
||||
String folderName = zipName.toLowerCase().endsWith(".zip")
|
||||
? zipName.substring(0, zipName.length() - 4)
|
||||
: zipName;
|
||||
// 如果有文件夹,创建子目录(如 E:/yun/333/222/)
|
||||
destRoot = Paths.get(storageSourceConfig.getValue(), decompressionPath, zipName);
|
||||
destRoot = Paths.get(storageSourceConfig.getValue(), decompressionPath, folderName);
|
||||
} else {
|
||||
// 如果只有文件,直接解压到目标目录(如 E:/yun/333/)
|
||||
destRoot = Paths.get(storageSourceConfig.getValue(), decompressionPath);
|
||||
@ -1655,17 +1646,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 新增:统一解压入口方法(保持原有逻辑结构)
|
||||
private File unzipToTemp(Path zipFilePath, String baseDir) throws IOException {
|
||||
String fileName = zipFilePath.getFileName().toString();
|
||||
@ -1700,7 +1680,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 保持原有ZIP判断方法不变
|
||||
private boolean hasFolderInZip(Path zipFilePath) throws IOException {
|
||||
try (ZipFile zipFile = new ZipFile(zipFilePath.toFile())) {
|
||||
@ -1738,80 +1717,68 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 保持原有解压逻辑结构的ZIP实现
|
||||
// 修改后的ZIP解压方法(修复解压问题)
|
||||
private File unzipFile(Path sourcePath, String baseDir) throws IOException {
|
||||
// 创建目标目录
|
||||
Path destRoot = Paths.get(baseDir);
|
||||
Files.createDirectories(destRoot);
|
||||
|
||||
// 关键修复1:使用与压缩时相同的字符集
|
||||
try (ZipInputStream zis = new ZipInputStream(
|
||||
Files.newInputStream(sourcePath),
|
||||
StandardCharsets.UTF_8)) {
|
||||
|
||||
// 关键修复2:记录已处理路径防止重复
|
||||
Set<String> processedEntries = new HashSet<>();
|
||||
Files.newInputStream(sourcePath), StandardCharsets.UTF_8)) {
|
||||
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
String entryName = entry.getName();
|
||||
|
||||
// 过滤系统文件(与压缩时逻辑对应)
|
||||
if (entryName.startsWith("__MACOSX/")) {
|
||||
zis.closeEntry();
|
||||
try {
|
||||
// 跳过 macOS 系统文件
|
||||
if (entry.getName().startsWith("__MACOSX")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 关键修复3:路径标准化处理
|
||||
entryName = entryName.replace("\\", "/"); // 统一分隔符
|
||||
if (processedEntries.contains(entryName)) {
|
||||
// 标准化路径并处理目录标识
|
||||
String entryName = entry.getName()
|
||||
.replace("\\", "/")
|
||||
.replaceFirst("^/+", ""); // 去除开头的斜杠
|
||||
|
||||
// 检测是否为目录(兼容以'/'结尾的条目)
|
||||
boolean isDirectory = entry.isDirectory() || entry.getName().endsWith("/");
|
||||
|
||||
// 调整路径:去除顶层目录(假设所有文件在单一顶层目录下)
|
||||
if (entryName.contains("/")) {
|
||||
int firstSlash = entryName.indexOf('/');
|
||||
entryName = entryName.substring(firstSlash + 1);
|
||||
|
||||
// 若处理后名称为空,则跳过顶层目录条目
|
||||
if (entryName.isEmpty() && isDirectory) {
|
||||
continue;
|
||||
}
|
||||
processedEntries.add(entryName);
|
||||
|
||||
// 构建目标路径
|
||||
Path destPath = destRoot.resolve(entryName).normalize();
|
||||
validatePathSafetya(destPath, destRoot);
|
||||
|
||||
// 日志跟踪
|
||||
LOGGER.debug("[解压] 处理条目:{} → {}", entryName, destPath);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
Files.createDirectories(destPath);
|
||||
LOGGER.debug("创建目录:{}", destPath);
|
||||
} else {
|
||||
// 关键修复4:确保父目录存在
|
||||
Path parentDir = destPath.getParent();
|
||||
if (!Files.exists(parentDir)) {
|
||||
Files.createDirectories(parentDir);
|
||||
LOGGER.debug("创建父目录:{}", parentDir);
|
||||
}
|
||||
|
||||
// 关键修复5:使用与压缩时相同的写入方式
|
||||
try (BufferedOutputStream bos = new BufferedOutputStream(
|
||||
Files.newOutputStream(destPath,
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING))) {
|
||||
Path targetPath = destRoot.resolve(entryName).normalize();
|
||||
validatePathSafetya(targetPath, destRoot);
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
int len;
|
||||
while ((len = zis.read(buffer)) > 0) {
|
||||
bos.write(buffer, 0, len);
|
||||
// 处理目录
|
||||
if (isDirectory) {
|
||||
Files.createDirectories(targetPath);
|
||||
}
|
||||
// 立即刷新到磁盘
|
||||
bos.flush();
|
||||
LOGGER.info("已解压文件:{} (大小:{} bytes)",
|
||||
destPath, Files.size(destPath));
|
||||
// 处理文件
|
||||
else {
|
||||
if (Files.exists(targetPath)) {
|
||||
LOGGER.warn("文件已存在,跳过覆盖: {}", targetPath);
|
||||
continue;
|
||||
}
|
||||
// 确保父目录存在
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
try (OutputStream os = Files.newOutputStream(targetPath)) {
|
||||
IOUtils.copy(zis, os);
|
||||
}
|
||||
}
|
||||
zis.closeEntry();
|
||||
} finally {
|
||||
zis.closeEntry(); // 确保每个条目只关闭一次
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 最终验证
|
||||
LOGGER.info("解压完成,验证文件结构:");
|
||||
Files.walk(destRoot)
|
||||
.forEach(path -> LOGGER.info("-> {}", path));
|
||||
|
||||
return destRoot.toFile();
|
||||
}
|
||||
@ -1828,45 +1795,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
+ " 超出根目录 " + normalizedDest);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 安全写入文件(带缓冲)
|
||||
*/
|
||||
private void writeFile(ZipInputStream zis, Path destPath) throws IOException {
|
||||
try (BufferedOutputStream bos = new BufferedOutputStream(
|
||||
Files.newOutputStream(destPath,
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING
|
||||
)
|
||||
)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int len;
|
||||
while ((len = zis.read(buffer)) > 0) {
|
||||
bos.write(buffer, 0, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部类:存储 ZIP 条目信息
|
||||
*/
|
||||
private static class ZipEntryInfo {
|
||||
private final String name;
|
||||
private final boolean isDirectory;
|
||||
|
||||
public ZipEntryInfo(String name, boolean isDirectory) {
|
||||
this.name = name;
|
||||
this.isDirectory = isDirectory;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return isDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 新增:TAR.GZ解压实现(保持相同路径逻辑)
|
||||
@ -1935,36 +1863,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// /**
|
||||
// * 解析压缩包条目(支持多种格式)
|
||||
// */
|
||||
@ -2015,8 +1913,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
//上面这个方法会覆盖 TODO
|
||||
// private File unzipToTemp(Path zipFilePath, String baseDir) throws IOException {
|
||||
// // 1. 直接构建目标路径(baseDir/zipName)
|
||||
@ -3835,7 +3731,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
dto.setUrl(null);
|
||||
dto.setType(null);
|
||||
|
||||
}else{
|
||||
} else {
|
||||
dto.setUrl(fileItemResult.getUrl());
|
||||
//如果是压缩文件 类型就给zip
|
||||
boolean isValid = hasValidExtension(fileItemResult.getName(), sysDictionaryItems);
|
||||
@ -3931,8 +3827,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 封装发送数据的逻辑
|
||||
private void sendData(String token, String[] values, int lineCount) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
@ -3980,7 +3874,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
|
||||
TsFiles tsFiles = tsFilesMapper.selectById(id);
|
||||
// 1. 路径标准化与安全校验
|
||||
Path targetPath = validateAndNormalizePath(config.getValue()+tsFiles.getWorkPath()+tsFiles.getFileName());
|
||||
Path targetPath = validateAndNormalizePath(config.getValue() + tsFiles.getWorkPath() + tsFiles.getFileName());
|
||||
|
||||
StringBuilder content = new StringBuilder();
|
||||
|
||||
@ -3995,7 +3889,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 验证路径是否合法并转换为标准化路径
|
||||
*/
|
||||
@ -4029,7 +3922,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
|
||||
TsFiles tsFiles = tsFilesMapper.selectById(id);
|
||||
// 1. 路径标准化与安全校验
|
||||
Path targetPath = validateAndNormalizePath(config.getValue()+tsFiles.getWorkPath()+tsFiles.getFileName());
|
||||
Path targetPath = validateAndNormalizePath(config.getValue() + tsFiles.getWorkPath() + tsFiles.getFileName());
|
||||
// 2. 确保父目录存在
|
||||
Path parentDir = targetPath.getParent();
|
||||
if (parentDir != null && !Files.exists(parentDir)) {
|
||||
|
Loading…
Reference in New Issue
Block a user