From 22f7188561a802fffbc7939eb2ce85c2bd7f70ee Mon Sep 17 00:00:00 2001 From: MoeexT Date: Wed, 1 Apr 2026 16:43:28 +0800 Subject: [PATCH] fix: upload task display error --- .../DatasetFileApplicationService.java | 36 +++++++++++++++++-- frontend/src/hooks/useSliceUpload.tsx | 14 +++++--- .../src/pages/DataManagement/dataset.model.ts | 2 ++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java index a6f86feb0..4427c3898 100644 --- a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java @@ -112,9 +112,41 @@ public PagedResponse getDatasetFilesWithDirectory(String datasetId, return PagedResponse.of(new Page<>(page, size)); } String datasetPath = dataset.getPath(); - Path queryPath = Path.of(dataset.getPath() + File.separator + prefix); + + // 规范化 prefix:去掉首尾斜杠,处理空字符串 + prefix = Optional.ofNullable(prefix).orElse("").trim(); + prefix = prefix.replace("\\", "/"); + while (prefix.startsWith("/")) { + prefix = prefix.substring(1); + } + while (prefix.endsWith("/")) { + prefix = prefix.substring(0, prefix.length() - 1); + } + + // 使用 Path API 安全地构建路径 + Path basePath = Paths.get(datasetPath); + Path queryPath = prefix.isEmpty() ? basePath : basePath.resolve(prefix).normalize(); + + // 确保查询路径在数据集路径下(防止路径遍历) + if (!queryPath.startsWith(basePath)) { + log.warn("Invalid prefix path: datasetId={}, prefix={}, queryPath={}", datasetId, prefix, queryPath); + return PagedResponse.of(new Page<>(page, size)); + } + Map datasetFilesMap = datasetFileRepository.findAllByDatasetId(datasetId) .stream().collect(Collectors.toMap(DatasetFile::getFilePath, Function.identity())); + + // 检查路径是否存在 + if (!Files.exists(queryPath)) { + log.warn("Dataset path does not exist: datasetId={}, path={}, queryPath={}", datasetId, datasetPath, queryPath); + return PagedResponse.of(new Page<>(page, size)); + } + + if (!Files.isDirectory(queryPath)) { + log.warn("Dataset path is not a directory: datasetId={}, queryPath={}", datasetId, queryPath); + return PagedResponse.of(new Page<>(page, size)); + } + try (Stream pathStream = Files.list(queryPath)) { List allFiles = pathStream .filter(path -> path.toString().startsWith(datasetPath)) @@ -140,7 +172,7 @@ public PagedResponse getDatasetFilesWithDirectory(String datasetId, return PagedResponse.of(page, size, total, totalPages, datasetFiles); } catch (IOException e) { - log.warn("list dataset path error"); + log.error("Failed to list dataset files: datasetId={}, queryPath={}", datasetId, queryPath, e); return PagedResponse.of(new Page<>(page, size)); } } diff --git a/frontend/src/hooks/useSliceUpload.tsx b/frontend/src/hooks/useSliceUpload.tsx index 9d48d9207..b8f325ecf 100644 --- a/frontend/src/hooks/useSliceUpload.tsx +++ b/frontend/src/hooks/useSliceUpload.tsx @@ -25,8 +25,11 @@ export function useFileSliceUpload( const { dataset } = detail; const title = `${t('hooks.sliceUpload.uploadDataset')}: ${dataset.name} `; const controller = new AbortController(); + // 使用 dataset.id + 时间戳 + 随机数确保每个上传任务有唯一的 key + const uniqueKey = `${dataset.id}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`; const task: TaskItem = { - key: dataset.id, + key: uniqueKey, + datasetId: dataset.id, // 保存原始 datasetId 用于 API 调用 title, percent: 0, reqId: -1, @@ -92,7 +95,7 @@ export function useFileSliceUpload( if (!task) { return; } - const { reqId, key } = task; + const { reqId, key, datasetId } = task; const { loaded, i, j, files, totalSize } = fileInfo; const formData = await buildFormData({ file: files[i], @@ -102,7 +105,8 @@ export function useFileSliceUpload( }); let newTask = { ...task }; - await uploadChunk(key, formData, { + // 使用 datasetId 调用 API,key 用于唯一标识任务 + await uploadChunk(datasetId || key, formData, { onUploadProgress: (e) => { const loadedSize = loaded + e.loaded; const curPercent = Number((loadedSize / totalSize) * 100).toFixed(2); @@ -120,10 +124,10 @@ export function useFileSliceUpload( async function uploadFile({ task, files, totalSize }) { console.log('[useSliceUpload] Calling preUpload with prefix:', task.prefix); - const { data: reqId } = await preUpload(task.key, { + const { data: reqId } = await preUpload(task.datasetId || task.key, { totalFileNum: files.length, totalSize, - datasetId: task.key, + datasetId: task.datasetId || task.key, hasArchive: task.hasArchive, prefix: task.prefix, }); diff --git a/frontend/src/pages/DataManagement/dataset.model.ts b/frontend/src/pages/DataManagement/dataset.model.ts index 8aef06c65..8e4f69979 100644 --- a/frontend/src/pages/DataManagement/dataset.model.ts +++ b/frontend/src/pages/DataManagement/dataset.model.ts @@ -95,6 +95,7 @@ export interface DatasetTask { export interface TaskItem { key: string; + datasetId?: string; // 数据集 ID(用于 API 调用) title: string; percent: number; reqId: number; @@ -104,4 +105,5 @@ export interface TaskItem { updateEvent?: string; size?: number; hasArchive?: boolean; + prefix?: string; // 当前路径前缀 }