吉安感知网项目-后端
linwei
2026-06-04 ad935c07a6cabf05fed9c615c7bd4f67f6d65293
drone-ops/drone-resource/src/main/java/org/sxkj/resource/service/impl/AttachServiceImpl.java
@@ -51,6 +51,7 @@
import org.sxkj.resource.builder.OssBuilder;
import org.sxkj.resource.dto.WaylineJobInfoQueryParam;
import org.sxkj.resource.entity.Attach;
import org.sxkj.resource.entity.Oss;
import org.sxkj.resource.mapper.AttachMapper;
import org.sxkj.resource.model.FileMetadataDTO;
import org.sxkj.resource.model.MinioPojo;
@@ -115,7 +116,12 @@
   @Override
   public IPage<AttachVO> selectAttachPage(IPage<AttachVO> page, AttachPageParam attach) {
       List<AttachVO> attachVOS   = baseMapper.selectAttachPage(page, attach);
      // 创建部门
      if (!AuthUtil.isAdministrator()) {
         List<Long> deptList = SysCache.getDeptChildIds(Long.valueOf(AuthUtil.getDeptId()));
         attach.setDeptList(deptList);
      }
      List<AttachVO> attachVOS = baseMapper.selectAttachPage(page, attach);
      return page.setRecords(attachVOS);
   }
@@ -125,11 +131,11 @@
      attach.setAreaCode(HeaderUtils.getAreaCodeHeaderAreaCode(attach.getAreaCode()));
      if (Objects.isNull(attach.getResultTypes())) {
         // 使用常量定义避免魔法数字
         attach.setResultTypes(Arrays.asList(TypeOfOutcome.IMAGE.getType(),
            TypeOfOutcome.VIDEO.getType(),
            TypeOfOutcome.AI.getType(),
            TypeOfOutcome.SURVEY.getType(),
            TypeOfOutcome.PANORAMA.getType()));
         // attach.setResultTypes(Arrays.asList(TypeOfOutcome.IMAGE.getType(),
         //    TypeOfOutcome.VIDEO.getType(),
         //    TypeOfOutcome.AI.getType(),
         //    TypeOfOutcome.SURVEY.getType(),
         //    TypeOfOutcome.PANORAMA.getType()));
      }
      List<Long> deptIdList = SysCache.getDeptChildIds(Long.valueOf(AuthUtil.getDeptId()));
      String permissionCondition = permissionBuilder.buildDataPermissionCondition(AuthUtil.getUserId(), "attach");
@@ -529,7 +535,6 @@
   @Override
   public Long findResultNumByJobId(String jobId) {
      return baseMapper.selectCount(Wrappers.<Attach>lambdaQuery()
         .eq(Attach::getIsGenerateAiImg, 0)
         .eq(Attach::getIsDeleted, 0)
         .eq(Attach::getPatrolTaskId, jobId).in(Attach::getResultType, AttachResultTypeEnum.getTheAttachmentType()));
   }
@@ -781,66 +786,67 @@
   /**
    * 下载附件
    * @param param 下载参数
    *
    * @param param        下载参数
    * @param outputStream 输出流
    * @return
    * @throws IOException
    * @return 下载是否成功
    * @throws IOException IO异常
    */
   @Override
   public Boolean downloadByByte(AttachmentDownloadParam param, OutputStream outputStream) throws IOException {
      // 如果附件id集合并且任务id集合参数都为空,就判断起止时间是否都有为空,如果都为空,则返回错误
      param.setAreaCode(HeaderUtils.formatAreaCode(param.getAreaCode()));
      if (CollectionUtils.isEmpty(param.getAttachIds()) && CollectionUtils.isEmpty(param.getWayLineJobIds())) {
         // 判断起止时间是否为空
         if (StringUtils.isEmpty(param.getStartTime()) || StringUtils.isEmpty(param.getEndTime())) {
            return false;
         }
      }
      // 获取附件列表
      List<AttachVO> attachList = getAttachList(param);
   public Boolean downloadByByte(String param, OutputStream outputStream) throws IOException {
      // 步骤1:构建下载参数并获取附件列表
      AttachmentDownloadParam attachmentDownloadParam = new AttachmentDownloadParam();
      attachmentDownloadParam.setAttachIds(Func.toLongList(param));
      List<AttachVO> attachList = getAttachList(attachmentDownloadParam);
      if (CollectionUtils.isEmpty(attachList)) {
         return false;
      }
      // 步骤2:获取当前租户ID
      String tenantId = AuthUtil.getTenantId();
      if (StringUtils.isBlank(tenantId)) {
         tenantId = "000000";
      }
      // 步骤3:通过OssBuilder获取OSS配置
      Oss oss = ossBuilder.getOss(tenantId, "");
      try {
         // 直接使用传入的输出流创建ZipOutputStream
         // 步骤4:创建ZipOutputStream并设置压缩级别
         ZipOutputStream zos = new ZipOutputStream(outputStream);
         // 使用BEST_SPEED而不是BEST_COMPRESSION,提高速度
         zos.setLevel(Deflater.BEST_SPEED);
         // 使用较小的缓冲区,减少内存使用
         final int BUFFER_SIZE = 1024 * 1024; // 1MB
         // 步骤5:设置缓冲区大小
         final int BUFFER_SIZE = 1024 * 1024;
         byte[] buffer = new byte[BUFFER_SIZE];
         // 创建Minio客户端
         // 步骤6:根据OSS配置创建MinioClient
         MinioClient minioClient = MinioClient.builder()
            .endpoint(minioPojo.getPath())
            .credentials(minioPojo.getAccessKey(), minioPojo.getSecretKey())
            .endpoint(oss.getEndpoint())
            .credentials(oss.getAccessKey(), oss.getSecretKey())
            .build();
         // 创建一个Set来跟踪已经使用的文件名,避免重名
         // 步骤7:创建文件名集合用于去重
         Set<String> usedFileNames = new HashSet<>();
         // 直接遍历所有附件,不再按任务分组
         // 步骤8:遍历附件列表,逐个下载并打包
         for (AttachVO attachVO : attachList) {
            String objectName = attachVO.getName();
            try {
               // 从MinIO获取文件流
               // 步骤8.1:从MinIO获取文件流
               InputStream is = minioClient.getObject(
                  GetObjectArgs.builder()
                     .bucket(minioPojo.getBucket())
                     .bucket(oss.getBucketName())
                     .object(objectName)
                     .build());
               // 提取文件名(不包含路径)
               // 步骤8.2:提取文件名(不包含路径)
               String fileName = objectName.substring(objectName.lastIndexOf("/") + 1);
               // 处理文件名冲突
               // 步骤8.3:处理文件名冲突
               String uniqueFileName = fileName;
               int counter = 1;
               while (usedFileNames.contains(uniqueFileName)) {
                  // 如果文件名已存在,在文件名和扩展名之间添加计数器
                  int dotIndex = fileName.lastIndexOf(".");
                  if (dotIndex > 0) {
                     uniqueFileName = fileName.substring(0, dotIndex) + "_" + counter + fileName.substring(dotIndex);
@@ -850,40 +856,38 @@
                  counter++;
               }
               // 将文件名添加到已使用集合中
               // 步骤8.4:记录已使用的文件名
               usedFileNames.add(uniqueFileName);
               // 创建zip条目(直接放在根目录下)
               // 步骤8.5:创建ZIP条目并写入文件内容
               ZipEntry zipEntry = new ZipEntry(uniqueFileName);
               zos.putNextEntry(zipEntry);
               // 将文件内容写入zip
               int length;
               while ((length = is.read(buffer)) > 0) {
                  zos.write(buffer, 0, length);
                  // 每写入一块数据后刷新,确保数据及时发送到客户端
                  zos.flush();
               }
               // 关闭资源
               // 步骤8.6:关闭当前条目和输入流
               zos.closeEntry();
               is.close();
            } catch (Exception e) {
               log.error("处理文件 {} 失败: {}", objectName, e.getMessage());
            }
         }
         // 确保所有数据都被写入
         // 步骤9:完成ZIP文件写入
         zos.finish();
         zos.flush();
         // 注意:不要关闭ZipOutputStream,因为它会关闭底层的OutputStream
         // 让调用者负责关闭输出流
         return true;
      } catch (Exception e) {
         log.error("创建zip文件失败", e);
         return false;
      }finally {
      } finally {
         // 步骤10:确保输出流被正确关闭
         if (outputStream != null) {
            try {
               outputStream.flush();