| | |
| | | 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; |
| | |
| | | |
| | | @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); |
| | | } |
| | | |
| | |
| | | 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"); |
| | |
| | | @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())); |
| | | } |
| | |
| | | |
| | | /** |
| | | * 下载附件 |
| | | * @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); |
| | |
| | | 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(); |