diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java index fdccafa..65496b8 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java @@ -521,28 +521,6 @@ public class TsFilesServiceImpl extends ServiceImpl impl if (count > 0) { 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.setStorageKey("local"); @@ -1120,6 +1098,9 @@ public class TsFilesServiceImpl extends ServiceImpl impl QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name", "filePath"); StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper); + compressedPath = normalizePath(compressedPath); + + String returnResult = ""; // 创建要压缩的文件夹列表 List sourceDirs = new ArrayList<>(); // 修复:使用 ArrayList @@ -1147,6 +1128,16 @@ public class TsFilesServiceImpl extends ServiceImpl impl // 添加到压缩列表 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 是根目录,使用当前工作目录 @@ -1163,7 +1154,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl // 设置压缩级别,可以选择调整 // zipOut.setLevel(ZipArchiveOutputStream.STORED); // 或者使用其他的压缩级别 // 调用压缩方法 - Boolean value = compressToSameDirectory(sourceDirs, compressedFormat); + Boolean value = compressToSameDirectory(sourceDirs, compressedFormat, zipFilePath); if (value) { //表结构增加 nodeId, String TaskId TsFiles tsFiles = new TsFiles(); @@ -1244,12 +1235,104 @@ public class TsFilesServiceImpl extends ServiceImpl 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 pathSegments = Arrays.stream(compressedPath.split("/")) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); - private Boolean compressToSameDirectory(List sourcePaths, String compressedFormat) { + // 校验路径是否以nodeId开头 + if (pathSegments.isEmpty() || !pathSegments.get(0).equals(nodeId)) { + throw new RuntimeException("路径必须包含当前节点ID"); + } + + // 提取实际要处理的目录层级(去掉开头的nodeId) + List 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() + .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 sourcePaths, String compressedFormat, Path outputPath) { try { switch (compressedFormat.toUpperCase()) { case "ZIP": - return compressToZip(sourcePaths); + return compressToZip(sourcePaths, outputPath); case "TAR.GZ": return compressToTarGz(sourcePaths); // case "7Z": @@ -1267,40 +1350,57 @@ public class TsFilesServiceImpl extends ServiceImpl impl /** - * 压缩多个文件/目录到单个ZIP + * 压缩文件/目录到指定路径 + * + * @param sourcePaths 源文件/目录列表 + * @param outputPath 指定的输出路径(可以是目录或完整文件路径) */ - public boolean compressToZip(List sourcePaths) { + public boolean compressToZip(List sourcePaths, Path outputPath) { if (sourcePaths.isEmpty()) { LOGGER.warn("源路径列表为空,无法压缩"); return false; } - Path zipPath = generateZipPath(sourcePaths.get(0)); - addedEntries.clear(); // 清空历史记录 + // 生成最终 ZIP 路径 + 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); for (Path sourcePath : sourcePaths) { if (Files.isDirectory(sourcePath)) { - // 使用唯一化的 baseDir(例如目录名) String baseDir = sourcePath.getFileName().toString(); addDirectoryToZip(sourcePath, zipOut, baseDir); } else { - // 文件直接添加到根目录,或指定唯一子目录 addFileToZip(sourcePath, zipOut, ""); } } zipOut.finish(); - return validateZip(zipPath); + return validateZip(finalZipPath); } catch (Exception e) { LOGGER.error("压缩失败", e); - deleteQuietly(zipPath); + deleteQuietly(finalZipPath); 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 { final Path sourceDir = dir; @@ -1555,9 +1655,18 @@ public class TsFilesServiceImpl extends ServiceImpl impl QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name", "filePath"); StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper); - + decompressionPath = normalizePath(decompressionPath); // 1. 获取压缩包记录 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 try { // 2. 判断压缩包类型(单文件还是文件夹) @@ -2181,22 +2290,36 @@ public class TsFilesServiceImpl extends ServiceImpl impl // 辅助方法:处理文件列表(并行安全) private void processFileLists(TsFiles tsFile, List minioList, List localList) throws Exception { - processFileList(tsFile.getBackupPath(), tsFile.getFileName(), "minio", minioList, tsFile.getIsFile()); - processFileList(tsFile.getWorkPath(), tsFile.getFileName(), "local", localList, tsFile.getIsFile()); + processFileList(tsFile.getBackupPath(), tsFile.getFileName(), "minio", minioList, tsFile.getIsFile(), tsFile.getNodeId(), tsFile.getTaskId()); + processFileList(tsFile.getWorkPath(), tsFile.getFileName(), "local", localList, tsFile.getIsFile(), tsFile.getNodeId(), tsFile.getTaskId()); } // 辅助方法:获取文件列表并添加到集合 - private void processFileList(String path, String fileName, String storageKey, List targetList, String isFile) throws Exception { + private void processFileList(String path, String fileName, String storageKey, List targetList, String isFile, String nodeId, String taskId) throws Exception { if (StringUtils.isNotEmpty(path)) { AbstractBaseFileService service = storageSourceContext.getByStorageKey(storageKey); if (isFile.equals("FOLDER") && storageKey.equals("minio")) { List files = service.fileListData(path, fileName + "/"); if (files != null) { // 对每个文件的路径进行规范化 - files.forEach(file -> { + for (FileItemResult file : files) { String normalizedPath = ensurePathFormat(file.getPath()); file.setPath(normalizedPath); - }); + QueryWrapper 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) { targetList.addAll(files); @@ -2206,10 +2329,19 @@ public class TsFilesServiceImpl extends ServiceImpl impl List files = service.fileListData(path, fileName); if (files != null) { // 对每个文件的路径进行规范化 - files.forEach(file -> { + for (FileItemResult file : files) { String normalizedPath = ensurePathFormat(file.getPath()); file.setPath(normalizedPath); - }); + QueryWrapper 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) { targetList.addAll(files); diff --git a/java/src/main/java/com/yfd/platform/modules/storage/model/result/FileItemResult.java b/java/src/main/java/com/yfd/platform/modules/storage/model/result/FileItemResult.java index 2cf6e52..2a04220 100644 --- a/java/src/main/java/com/yfd/platform/modules/storage/model/result/FileItemResult.java +++ b/java/src/main/java/com/yfd/platform/modules/storage/model/result/FileItemResult.java @@ -43,7 +43,7 @@ public class FileItemResult implements Serializable { private String minioMd5; private String id; private String formattedTime; - + private String parentId; /** * 获取路径和名称的组合, 并移除重复的路径分隔符 /. *