提交代码压缩解压缩到指定目录代码提交

This commit is contained in:
lilin 2025-03-24 17:41:42 +08:00
parent 76b5faf473
commit 5dae66f63e
2 changed files with 175 additions and 43 deletions

View File

@ -521,28 +521,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
if (count > 0) { if (count > 0) {
return ResponseResult.error("文件夹名称已存在!"); return ResponseResult.error("文件夹名称已存在!");
} }
// //判断文件夹是否创建
// AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey("local");
// boolean flag = fileService.isFolderCreated(File.separator + tsFiles.getNodeId());
// //如果是false 说明没有创建 那就新建一个文件夹
// if(!flag){
// //本地创建文件夹
// NewFolderRequest newFolderRequest = new NewFolderRequest();
// newFolderRequest.setStorageKey("local");
// newFolderRequest.setPath(tsFiles.getWorkPath());
// newFolderRequest.setPassword(null);
// newFolderRequest.setName(tsFiles.getFileName());
//
// AbstractBaseFileService<?> fileServiceData = storageSourceContext.getByStorageKey(newFolderRequest.getStorageKey());
// boolean flagData = fileServiceData.newFolder(newFolderRequest.getPath(), newFolderRequest.getName());
// if(!flagData){
// LOGGER.error("创建节点文件夹失败!");
// return ResponseResult.error("新增文件夹失败!");
// }
// }
//本地创建文件夹 //本地创建文件夹
NewFolderRequest newFolderRequest = new NewFolderRequest(); NewFolderRequest newFolderRequest = new NewFolderRequest();
newFolderRequest.setStorageKey("local"); newFolderRequest.setStorageKey("local");
@ -1120,6 +1098,9 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
QueryWrapper<StorageSourceConfig> queryWrapper = new QueryWrapper<>(); QueryWrapper<StorageSourceConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "filePath"); queryWrapper.eq("name", "filePath");
StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper); StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper);
compressedPath = normalizePath(compressedPath);
String returnResult = ""; String returnResult = "";
// 创建要压缩的文件夹列表 // 创建要压缩的文件夹列表
List<Path> sourceDirs = new ArrayList<>(); // 修复使用 ArrayList List<Path> sourceDirs = new ArrayList<>(); // 修复使用 ArrayList
@ -1147,6 +1128,16 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 添加到压缩列表 // 添加到压缩列表
sourceDirs.add(path); // 修复使用可变集合 sourceDirs.add(path); // 修复使用可变集合
} }
try {
String finalParentId = ensureFullPathExists(compressedPath, filesList.get(0).getNodeId(), filesList.get(0).getTaskId());
parentId = finalParentId;
LOGGER.info("路径已全部存在最终目录ID: {}", finalParentId);
} catch (RuntimeException e) {
LOGGER.error("路径创建失败: {}", e.getMessage());
returnResult = "路径创建失败!";
return returnResult;
}
// 规范化压缩包存放路径 // 规范化压缩包存放路径
// 如果 compressedPath 是根目录使用当前工作目录 // 如果 compressedPath 是根目录使用当前工作目录
@ -1163,7 +1154,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 设置压缩级别可以选择调整 // 设置压缩级别可以选择调整
// zipOut.setLevel(ZipArchiveOutputStream.STORED); // 或者使用其他的压缩级别 // zipOut.setLevel(ZipArchiveOutputStream.STORED); // 或者使用其他的压缩级别
// 调用压缩方法 // 调用压缩方法
Boolean value = compressToSameDirectory(sourceDirs, compressedFormat); Boolean value = compressToSameDirectory(sourceDirs, compressedFormat, zipFilePath);
if (value) { if (value) {
//表结构增加 nodeId, String TaskId //表结构增加 nodeId, String TaskId
TsFiles tsFiles = new TsFiles(); TsFiles tsFiles = new TsFiles();
@ -1244,12 +1235,104 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// ================== 核心方法改造 ================== // ================== 核心方法改造 ==================
/**
* 确保完整路径存在不存在则逐层创建
*
* @param compressedPath 输入的完整路径 /38240bbb160450e03de33df911937d59/1/1/2/3/4/
* @param nodeId 当前节点ID 38240bbb160450e03de33df911937d59
* @return 最终目录的数据库ID即最后一级目录的ID
*/
public String ensureFullPathExists(String compressedPath, String nodeId, String taskId) {
// 1. 解析路径提取有效层级目录名跳过nodeId部分
List<String> pathSegments = Arrays.stream(compressedPath.split("/"))
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
private Boolean compressToSameDirectory(List<Path> sourcePaths, String compressedFormat) { // 校验路径是否以nodeId开头
if (pathSegments.isEmpty() || !pathSegments.get(0).equals(nodeId)) {
throw new RuntimeException("路径必须包含当前节点ID");
}
// 提取实际要处理的目录层级去掉开头的nodeId
List<String> dirSegments = pathSegments.subList(1, pathSegments.size());
if (dirSegments.isEmpty()) {
throw new RuntimeException("路径缺少有效目录层级");
}
// 2. 初始化根目录信息基于nodeId
String parentId = "00"; // 根目录的父ID为0
String baseFsPath = "/" + nodeId + "/"; // 本地存储基础路径
// 设置当前时间
LocalDateTime now = LocalDateTime.now();
// 转换为 Timestamp
Timestamp currentTime = Timestamp.valueOf(now);
//上传人是当前登录人
UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
LoginUser loginuser = (LoginUser) authentication.getPrincipal();
// 3. 逐层处理目录从第一个有效目录开始
String currentPath = baseFsPath;
for (String dirName : dirSegments) {
// 检查当前层级目录是否存在
TsFiles dir = tsFilesMapper.selectOne(new QueryWrapper<TsFiles>()
.eq("node_id", nodeId)
.eq("task_id", taskId)
.eq("parent_id", parentId)
.eq("work_path", currentPath)
.eq("file_name", dirName));
if (dir == null) {
// 创建本地文件夹 /38240bbb.../1/2/
// //本地创建文件夹
NewFolderRequest newFolderRequest = new NewFolderRequest();
newFolderRequest.setStorageKey("local");
newFolderRequest.setPath(currentPath);
newFolderRequest.setPassword(null);
newFolderRequest.setName(dirName);
AbstractBaseFileService<?> fileServicefolder = storageSourceContext.getByStorageKey(newFolderRequest.getStorageKey());
boolean flagfolder = fileServicefolder.newFolder(newFolderRequest.getPath(), newFolderRequest.getName());
if (!flagfolder) {
throw new RuntimeException("目录创建失败: " + dirName);
}
// 插入数据库记录
TsFiles tsFiles1 = new TsFiles();
tsFiles1.setNodeId(nodeId);
tsFiles1.setTaskId(taskId);
tsFiles1.setIsFile("FOLDER");
tsFiles1.setParentId(parentId);
tsFiles1.setFileName(dirName);
tsFiles1.setFileSize("0");
tsFiles1.setWorkPath(currentPath);
tsFiles1.setBackupPath("");
tsFiles1.setKeywords("");
tsFiles1.setDescription("");
tsFiles1.setUploadTime(currentTime);
tsFiles1.setUploader(loginuser.getUsername());
//如果为空就新增
int valueAdded = tsFilesMapper.insert(tsFiles1);
if (valueAdded == 1) {
parentId = tsFiles1.getId();
currentPath = tsFiles1.getWorkPath() + tsFiles1.getFileName() + "/";
}
} else {
parentId = dir.getId();
currentPath = dir.getWorkPath() + dir.getFileName() + "/";
}
}
return parentId;
}
private Boolean compressToSameDirectory(List<Path> sourcePaths, String compressedFormat, Path outputPath) {
try { try {
switch (compressedFormat.toUpperCase()) { switch (compressedFormat.toUpperCase()) {
case "ZIP": case "ZIP":
return compressToZip(sourcePaths); return compressToZip(sourcePaths, outputPath);
case "TAR.GZ": case "TAR.GZ":
return compressToTarGz(sourcePaths); return compressToTarGz(sourcePaths);
// case "7Z": // case "7Z":
@ -1267,40 +1350,57 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
/** /**
* 压缩多个文件/目录到单个ZIP * 压缩文件/目录到指定路径
*
* @param sourcePaths 源文件/目录列表
* @param outputPath 指定的输出路径可以是目录或完整文件路径
*/ */
public boolean compressToZip(List<Path> sourcePaths) { public boolean compressToZip(List<Path> sourcePaths, Path outputPath) {
if (sourcePaths.isEmpty()) { if (sourcePaths.isEmpty()) {
LOGGER.warn("源路径列表为空,无法压缩"); LOGGER.warn("源路径列表为空,无法压缩");
return false; return false;
} }
Path zipPath = generateZipPath(sourcePaths.get(0)); // 生成最终 ZIP 路径
addedEntries.clear(); // 清空历史记录 Path finalZipPath = generateFinalZipPath(sourcePaths.get(0), outputPath);
addedEntries.clear();
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipPath))) { try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(finalZipPath))) {
zipOut.setLevel(Deflater.DEFAULT_COMPRESSION); zipOut.setLevel(Deflater.DEFAULT_COMPRESSION);
for (Path sourcePath : sourcePaths) { for (Path sourcePath : sourcePaths) {
if (Files.isDirectory(sourcePath)) { if (Files.isDirectory(sourcePath)) {
// 使用唯一化的 baseDir例如目录名
String baseDir = sourcePath.getFileName().toString(); String baseDir = sourcePath.getFileName().toString();
addDirectoryToZip(sourcePath, zipOut, baseDir); addDirectoryToZip(sourcePath, zipOut, baseDir);
} else { } else {
// 文件直接添加到根目录或指定唯一子目录
addFileToZip(sourcePath, zipOut, ""); addFileToZip(sourcePath, zipOut, "");
} }
} }
zipOut.finish(); zipOut.finish();
return validateZip(zipPath); return validateZip(finalZipPath);
} catch (Exception e) { } catch (Exception e) {
LOGGER.error("压缩失败", e); LOGGER.error("压缩失败", e);
deleteQuietly(zipPath); deleteQuietly(finalZipPath);
return false; return false;
} }
} }
/**
* 生成最终 ZIP 路径
*/
private Path generateFinalZipPath(Path sourcePath, Path outputPath) {
// 如果输出路径是目录则在目录中生成默认名称的 ZIP
if (Files.isDirectory(outputPath)) {
String baseName = sourcePath.getFileName().toString().replaceFirst("[.][^.]+$", "");
return outputPath.resolve(baseName + ".zip");
}
// 否则直接使用指定路径
return outputPath;
}
private void addDirectoryToZip(Path dir, ZipOutputStream zipOut, String baseDir) throws IOException { private void addDirectoryToZip(Path dir, ZipOutputStream zipOut, String baseDir) throws IOException {
final Path sourceDir = dir; final Path sourceDir = dir;
@ -1555,9 +1655,18 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
QueryWrapper<StorageSourceConfig> queryWrapper = new QueryWrapper<>(); QueryWrapper<StorageSourceConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "filePath"); queryWrapper.eq("name", "filePath");
StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper); StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper);
decompressionPath = normalizePath(decompressionPath);
// 1. 获取压缩包记录 // 1. 获取压缩包记录
TsFiles zipFileRecord = tsFilesMapper.selectById(id); TsFiles zipFileRecord = tsFilesMapper.selectById(id);
try {
String finalParentId = ensureFullPathExists(decompressionPath, zipFileRecord.getNodeId(), zipFileRecord.getTaskId());
parentId = finalParentId;
LOGGER.info("路径已全部存在最终目录ID: {}", finalParentId);
} catch (RuntimeException e) {
LOGGER.error("路径创建失败: {}", e.getMessage());
return "路径创建失败!";
}
String zipName = getFileNameWithoutExtension(zipFileRecord.getFileName()); // 示例222 String zipName = getFileNameWithoutExtension(zipFileRecord.getFileName()); // 示例222
try { try {
// 2. 判断压缩包类型单文件还是文件夹 // 2. 判断压缩包类型单文件还是文件夹
@ -2181,22 +2290,36 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 辅助方法处理文件列表并行安全 // 辅助方法处理文件列表并行安全
private void processFileLists(TsFiles tsFile, List<FileItemResult> minioList, List<FileItemResult> localList) throws Exception { private void processFileLists(TsFiles tsFile, List<FileItemResult> minioList, List<FileItemResult> localList) throws Exception {
processFileList(tsFile.getBackupPath(), tsFile.getFileName(), "minio", minioList, tsFile.getIsFile()); processFileList(tsFile.getBackupPath(), tsFile.getFileName(), "minio", minioList, tsFile.getIsFile(), tsFile.getNodeId(), tsFile.getTaskId());
processFileList(tsFile.getWorkPath(), tsFile.getFileName(), "local", localList, tsFile.getIsFile()); processFileList(tsFile.getWorkPath(), tsFile.getFileName(), "local", localList, tsFile.getIsFile(), tsFile.getNodeId(), tsFile.getTaskId());
} }
// 辅助方法获取文件列表并添加到集合 // 辅助方法获取文件列表并添加到集合
private void processFileList(String path, String fileName, String storageKey, List<FileItemResult> targetList, String isFile) throws Exception { private void processFileList(String path, String fileName, String storageKey, List<FileItemResult> targetList, String isFile, String nodeId, String taskId) throws Exception {
if (StringUtils.isNotEmpty(path)) { if (StringUtils.isNotEmpty(path)) {
AbstractBaseFileService<?> service = storageSourceContext.getByStorageKey(storageKey); AbstractBaseFileService<?> service = storageSourceContext.getByStorageKey(storageKey);
if (isFile.equals("FOLDER") && storageKey.equals("minio")) { if (isFile.equals("FOLDER") && storageKey.equals("minio")) {
List<FileItemResult> files = service.fileListData(path, fileName + "/"); List<FileItemResult> files = service.fileListData(path, fileName + "/");
if (files != null) { if (files != null) {
// 对每个文件的路径进行规范化 // 对每个文件的路径进行规范化
files.forEach(file -> { for (FileItemResult file : files) {
String normalizedPath = ensurePathFormat(file.getPath()); String normalizedPath = ensurePathFormat(file.getPath());
file.setPath(normalizedPath); file.setPath(normalizedPath);
}); QueryWrapper<TsFiles> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("node_id", nodeId);
queryWrapper.eq("task_id", taskId);
queryWrapper.eq("backup_path", normalizedPath);
queryWrapper.eq("file_name", file.getName());
TsFiles tsFiles1 = tsFilesMapper.selectOne(queryWrapper);
if (tsFiles1 != null) {
file.setParentId(tsFiles1.getParentId());
}
}
// files.forEach(file -> {
//
// });
// 同步添加线程安全 // 同步添加线程安全
synchronized (targetList) { synchronized (targetList) {
targetList.addAll(files); targetList.addAll(files);
@ -2206,10 +2329,19 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
List<FileItemResult> files = service.fileListData(path, fileName); List<FileItemResult> files = service.fileListData(path, fileName);
if (files != null) { if (files != null) {
// 对每个文件的路径进行规范化 // 对每个文件的路径进行规范化
files.forEach(file -> { for (FileItemResult file : files) {
String normalizedPath = ensurePathFormat(file.getPath()); String normalizedPath = ensurePathFormat(file.getPath());
file.setPath(normalizedPath); file.setPath(normalizedPath);
}); QueryWrapper<TsFiles> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("node_id", nodeId);
queryWrapper.eq("task_id", taskId);
queryWrapper.eq("work_path", normalizedPath);
queryWrapper.eq("file_name", file.getName());
TsFiles tsFiles1 = tsFilesMapper.selectOne(queryWrapper);
if (tsFiles1 != null) {
file.setParentId(tsFiles1.getParentId());
}
}
// 同步添加线程安全 // 同步添加线程安全
synchronized (targetList) { synchronized (targetList) {
targetList.addAll(files); targetList.addAll(files);

View File

@ -43,7 +43,7 @@ public class FileItemResult implements Serializable {
private String minioMd5; private String minioMd5;
private String id; private String id;
private String formattedTime; private String formattedTime;
private String parentId;
/** /**
* 获取路径和名称的组合, 并移除重复的路径分隔符 /. * 获取路径和名称的组合, 并移除重复的路径分隔符 /.
* *