# 影像批量上传功能实现总结 ## 📋 功能概述 实现了支持文件夹、ZIP压缩包的高级批量上传功能,具备以下特性: 1. ✅ 支持单个文件上传 2. ✅ 支持多个文件上传 3. ✅ 支持文件夹上传 4. ✅ 支持ZIP压缩包上传 5. ✅ 自动识别并按study_instance_uid分组 6. ✅ 使用study_instance_uid作为存储目录 7. ✅ 支持多study的同一批上传 8. ✅ 使用batch_id和parent_study_id关联同一批上传的不同study --- ## 📁 文件存储结构变化 ### 旧结构 ``` ~/qconline/dicom/{institutionId}/{date}/{fileId}.dcm ``` ### 新结构 ``` ~/qconline/dicom/{institutionId}/{study_instance_uid}/{fileId}.dcm ``` **优势:** 同一个检查的所有文件都在同一个目录下,便于管理和访问 --- ## 🗄️ 数据库变化 ### 新增字段 **表:** `study_info` ```sql -- 添加批次ID字段 ALTER TABLE `study_info` ADD COLUMN `batch_id` VARCHAR(50) NULL COMMENT '批次ID:同一批上传的影像共享同一个batch_id' AFTER `parent_study_id`; -- 添加索引 ALTER TABLE `study_info` ADD INDEX `idx_batch_id` (`batch_id`); ``` **字段说明:** - `batch_id`: 批次ID,同一批上传的所有study共享同一个batch_id - `parent_study_id`: 父检查ID,第一个study为null,其他study指向第一个study **关联关系:** ``` 批次1: batch_id = "BATCH_1234567890" ├─ Study A (主study, parent_study_id = NULL) ├─ Study B (子study, parent_study_id = Study A的studyId) └─ Study C (子study, parent_study_id = Study A的studyId) ``` --- ## 🔧 后端实现 ### 1. 新增类文件 #### ArchiveUtil.java **路径:** `/src/main/java/com/zskk/qconline/modules/dicom/util/ArchiveUtil.java` **功能:** - 判断文件是否为压缩包(zip) - 解压ZIP文件到临时目录 - 递归查找DICOM文件 - 创建和清理临时目录 #### BatchUploadVO.java **路径:** `/src/main/java/com/zskk/qconline/modules/dicom/vo/BatchUploadVO.java` **功能:** - 批量上传结果封装 - 包含批次ID、文件总数、study数、study列表等信息 ### 2. 修改的类文件 #### DicomService.java **路径:** `/src/main/java/com/zskk/qconline/modules/dicom/service/DicomService.java` **新增方法:** ```java BatchUploadVO batchUploadAdvanced(MultipartFile[] files, String institutionId); ``` #### DicomServiceImpl.java **路径:** `/src/main/java/com/zskk/qconline/modules/dicom/service/impl/DicomServiceImpl.java` **新增方法:** - `batchUploadAdvanced()`: 高级批量上传主方法 - `groupFilesByStudyInstanceUid()`: 按study_instance_uid分组文件 - `saveStudyFiles()`: 保存文件到study_instance_uid目录 **核心逻辑:** 1. 识别上传的文件类型(普通文件或压缩包) 2. 如果是压缩包,先解压到临时目录 3. 解析所有DICOM文件的study_instance_uid 4. 按study_instance_uid分组 5. 为每个study创建StudyInfo记录 6. 第一个study作为主study(parent_study_id=null) 7. 其他study的parent_study_id指向主study 8. 所有study共享同一个batch_id #### StudyInfo.java **路径:** `/src/main/java/com/zskk/qconline/modules/entity/StudyInfo.java` **新增字段:** ```java @TableField("batch_id") private String batchId; ``` #### DicomController.java **路径:** `/src/main/java/com/zskk/qconline/modules/dicom/controller/DicomController.java` **新增接口:** ```java POST /api/dicom/batch-upload-advanced ``` **请求参数:** - `files`: MultipartFile[](文件数组) - `institutionId`: String(机构ID) **返回结果:** ```json { "code": 200, "message": "上传成功", "data": { "batchId": "BATCH_1234567890", "totalFiles": 150, "studyCount": 2, "studies": [ { "studyId": "STY1234567890", "studyInstanceUid": "1.2.840.113619.2.55.3...", "patientId": "P12345", "patientName": "张三", "modality": "CT", "bodyPart": "胸部", "fileCount": 100, "isPrimary": true }, { "studyId": "STY1234567891", "studyInstanceUid": "1.2.840.113619.2.55.3...", "patientId": "P12345", "patientName": "张三", "modality": "MR", "bodyPart": "颅脑", "fileCount": 50, "isPrimary": false } ] } } ``` --- ## 🚀 使用示例 ### 示例1:上传单个文件 ```bash curl -X POST http://localhost:8080/api/dicom/batch-upload-advanced \ -F "files=@image.dcm" \ -F "institutionId=INST001" ``` ### 示例2:上传ZIP压缩包 ```bash curl -X POST http://localhost:8080/api/dicom/batch-upload-advanced \ -F "files=@study.zip" \ -F "institutionId=INST001" ``` ### 示例3:上传多个文件(模拟文件夹) ```bash curl -X POST http://localhost:8080/api/dicom/batch-upload-advanced \ -F "files=@image1.dcm" \ -F "files=@image2.dcm" \ -F "files=@image3.dcm" \ -F "institutionId=INST001" ``` --- ## ⚙️ 配置说明 ### 临时目录配置 压缩包解压时会使用系统临时目录: ``` # macOS/Linux /tmp/dicom_upload_xxxxxxxx/ # Windows C:\Users\{username}\AppData\Local\Temp\dicom_upload_xxxxxxxx\ ``` ### 文件存储根目录 ```java private static final String DICOM_ROOT_PATH = System.getProperty("user.home") + "/qconline/dicom/"; ``` --- ## 📝 执行数据库迁移 ```bash # 连接到数据库 mysql -u root -p # 选择数据库 use qconline; # 执行SQL脚本 source /path/to/alter_study_info_add_batch_id.sql; ``` --- ## 🎯 核心特性说明 ### 1. 多Study自动识别 当上传的压缩包包含多个不同study_instance_uid的文件时: - 系统会自动识别并分组 - 为每个不同的study_instance_uid创建独立的StudyInfo记录 - 通过batch_id标识它们来自同一批上传 - 通过parent_study_id标识主从关系 ### 2. 文件去重 - 基于study_instance_uid判断是否已存在 - 如果已存在,可以选择覆盖或跳过(当前为覆盖逻辑) ### 3. 临时目录自动清理 - 上传过程中创建临时目录 - 上传完成后自动清理 - 即使发生异常也会在finally块中清理 ### 4. 事务保证 - 整个批量上传过程使用@Transactional - 任何步骤失败都会回滚 - 保证数据一致性 --- ## 🔄 兼容性 ### 保留旧接口 为了兼容现有系统,保留了旧的上传接口: - `/api/dicom/upload` - 单文件上传(旧) - `/api/dicom/batch-upload` - 批量上传(旧) ### 新接口 - `/api/dicom/batch-upload-advanced` - 高级批量上传(新) --- ## 📊 日志输出示例 ``` [INFO] 开始批量上传: institutionId=INST001, 文件数=1 [INFO] 批次ID: BATCH_1735189234567 [INFO] 检测到压缩包: study.zip [INFO] 创建临时目录: /var/folders/.../dicom_upload_a1b2c3d4 [INFO] 开始解压文件: /var/folders/.../study.zip [INFO] 解压完成,共解压 150 个文件 [INFO] 共收集到 150 个文件需要处理 [INFO] 识别到 2 个不同的study [INFO] 处理study: studyInstanceUid=1.2.840..., 文件数=100 [INFO] 创建study目录: /Users/xxx/qconline/dicom/INST001/1.2.840.../ [INFO] 创建study记录: studyId=STY001, studyInstanceUid=1.2.840..., 文件数=100 [INFO] 处理study: studyInstanceUid=1.2.841..., 文件数=50 [INFO] 创建study目录: /Users/xxx/qconline/dicom/INST001/1.2.841.../ [INFO] 创建study记录: studyId=STY002, studyInstanceUid=1.2.841..., 文件数=50 [INFO] 批量上传完成: batchId=BATCH_1735189234567, 总文件数=150, study数=2 [INFO] 清理临时目录: /var/folders/.../dicom_upload_a1b2c3d4 ``` --- ## ✅ 后端完成清单 - [x] 创建数据库迁移SQL脚本 - [x] 修改StudyInfo实体类,添加batchId字段 - [x] 创建ArchiveUtil压缩包工具类 - [x] 创建BatchUploadVO批量上传结果类 - [x] 修改DicomService接口,添加批量上传方法 - [x] 在DicomServiceImpl中实现批量上传逻辑 - [x] 实现按study_instance_uid分组逻辑 - [x] 实现文件存储结构(使用study_instance_uid命名目录) - [x] 在DicomController中添加批量上传接口 - [x] 添加batch_id和parent_study_id关联逻辑 --- ## 📌 前端修改建议 ### 1. 修改上传组件 使用Element Plus的el-upload组件,添加以下功能: - 支持选择文件夹(`webkitdirectory`属性) - 支持多文件选择 - 显示上传进度 - 显示上传结果 ### 2. API调用 使用新的批量上传接口: ```javascript const formData = new FormData() files.forEach(file => { formData.append('files', file) }) formData.append('institutionId', institutionId) axios.post('/api/dicom/batch-upload-advanced', formData) ``` --- ## 🎉 总结 后端功能已全部实现,包括: - ✅ 压缩包解压 - ✅ 多study识别和分组 - ✅ 文件存储结构优化 - ✅ batch_id和parent_study_id关联 - ✅ 完整的日志记录 - ✅ 异常处理和事务保证 - ✅ 临时目录自动清理 接下来需要修改前端上传组件以使用新接口。