From 3b42bea34b9ed7d9d2096c62dc261cc89bc31ae7 Mon Sep 17 00:00:00 2001
From: linwei <872216696@qq.com>
Date: Mon, 26 Jan 2026 18:25:30 +0800
Subject: [PATCH] 文件下载
---
drone-ops/drone-resource/src/main/java/org/sxkj/resource/service/impl/AttachServiceImpl.java | 192 +++++++++++++++++++++++++++++++++--------------
1 files changed, 133 insertions(+), 59 deletions(-)
diff --git a/drone-ops/drone-resource/src/main/java/org/sxkj/resource/service/impl/AttachServiceImpl.java b/drone-ops/drone-resource/src/main/java/org/sxkj/resource/service/impl/AttachServiceImpl.java
index 37c5f53..157605e 100644
--- a/drone-ops/drone-resource/src/main/java/org/sxkj/resource/service/impl/AttachServiceImpl.java
+++ b/drone-ops/drone-resource/src/main/java/org/sxkj/resource/service/impl/AttachServiceImpl.java
@@ -23,6 +23,8 @@
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import io.minio.GetObjectArgs;
+import io.minio.MinioClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
@@ -41,6 +43,7 @@
import org.sxkj.common.enums.AttachResultTypeEnum;
import org.sxkj.common.enums.TypeOfOutcome;
import org.sxkj.common.func.Streams;
+import org.sxkj.common.model.ResponseResult;
import org.sxkj.common.query.PaginationUtils;
import org.sxkj.common.redis.RedisOpsUtils;
import org.sxkj.common.utils.*;
@@ -52,6 +55,7 @@
import org.sxkj.resource.model.FileMetadataDTO;
import org.sxkj.resource.model.MinioPojo;
import org.sxkj.resource.model.PositionDTO;
+import org.sxkj.resource.param.AttachPageParam;
import org.sxkj.resource.service.IAttachService;
import org.sxkj.resource.util.MinioFileDownloader;
import org.sxkj.resource.vo.*;
@@ -62,6 +66,9 @@
import org.sxkj.tools.model.TifConvertResult;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
@@ -69,6 +76,9 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
/**
* 附件表 服务实现类
@@ -104,15 +114,8 @@
@Override
- public IPage<AttachVO> selectAttachPage(IPage<AttachVO> page, AttachVO attach) {
- Integer resultType = attach.getResultType();
- List<AttachVO> attachVOS = Collections.emptyList();
- if (Objects.nonNull(resultType) && Attach.RESULT_TYPE_AI.equals(resultType)) {
- attachVOS = baseMapper.selectEventAttachPage(page, attach);
- } else {
- attachVOS = baseMapper.selectAttachPage(page, attach);
- }
-
+ public IPage<AttachVO> selectAttachPage(IPage<AttachVO> page, AttachPageParam attach) {
+ List<AttachVO> attachVOS = baseMapper.selectAttachPage(page, attach);
return page.setRecords(attachVOS);
}
@@ -445,36 +448,6 @@
@Override
- public List<AnalysisAttachVo> contrastiveAnalysis(Long id) {
- Attach attach = getById(id);
- ValidUtil.assertNull(attach, "数据不能为空!");
- ValidUtil.assertNull(attach.getMetadata(), "媒体数据不存在经纬度!");
- FileMetadataDTO metadata = JSON.parseObject(attach.getMetadata().toString(), FileMetadataDTO.class);
- PositionDTO shootPosition = metadata.getShootPosition();
- ValidUtil.assertNull(shootPosition, "媒体数据不存在经纬度!");
- Double lat = attach.getLatitude() != null && attach.getLatitude() > 0 ? attach.getLatitude() : shootPosition.getLat();
- Double lng = attach.getLongitude() != null && attach.getLongitude() > 0 ? attach.getLongitude() : shootPosition.getLng();
- Date createTime = attach.getCreateTime();
- // 转换为 LocalDateTime(保留时间部分)
- LocalDateTime ldt = createTime.toInstant()
- .atZone(ZoneId.systemDefault())
- .toLocalDateTime();
-// 计算前后两个月
- LocalDateTime twoMonthsBefore = ldt.minusMonths(2); // 前2个月
- LocalDateTime twoMonthsAfter = ldt.plusMonths(2); // 后2个月
-
-// 如果需要转回 Date 类型
- Date beforeDate = Date.from(twoMonthsBefore.atZone(ZoneId.systemDefault()).toInstant());
- Date afterDate = Date.from(twoMonthsAfter.atZone(ZoneId.systemDefault()).toInstant());
- List<AnalysisAttachVo> pointAttachList = baseMapper.findPointAttach(lat, lng, beforeDate, afterDate, id);
- return Streams.toList(pointAttachList, entity -> {
- entity.setIsCheck(entity.getId().equals(id));
- return entity;
- });
-
- }
-
- @Override
public AttachInfoVO getAttachInfo(Long id) {
AttachInfoVO attachVO = baseMapper.getAttachInfo(id);
if (attachVO != null) {
@@ -484,16 +457,8 @@
PositionDTO shootPosition = metadata.getShootPosition();
ValidUtil.assertNull(shootPosition, "媒体数据不存在经纬度!");
Double lat = shootPosition.getLat();
- attachVO.setLatitude(lat);
Double lng = shootPosition.getLng();
- attachVO.setLongitude(lng);
}
- // 文件类型,通过文件名称有_T,则是红外,包含_W|_V 则是可见光
-// if (attachVO.getName().contains("_T")) {
-// attachVO.setPhotoType("ir");
-// } else if (attachVO.getName().contains("_W") || attachVO.getName().contains("_V")) {
-// attachVO.setPhotoType("visible");
-// }
}
return attachVO;
}
@@ -507,19 +472,8 @@
FileMetadataDTO metadata = JSON.parseObject(item.getMetadata().toString(), FileMetadataDTO.class);
PositionDTO shootPosition = metadata.getShootPosition();
Double lat = shootPosition.getLat();
- item.setLatitude(lat);
Double lng = shootPosition.getLng();
- item.setLongitude(lng);
}
- });
- // 把result_type=0和把result_type=2数据进行用经度和纬度对比,如果有相同的,则把result_type=0的数据删除
- boolean removed = attachVO.removeIf(item -> {
- return item.getResultType() == 0 &&
- attachVO.stream().anyMatch(
- item1 -> item1.getResultType() == 2
- && item1.getLatitude().equals(item.getLatitude())
- && item1.getLongitude().equals(item.getLongitude())
- );
});
return attachVO;
}
@@ -577,7 +531,7 @@
return baseMapper.selectCount(Wrappers.<Attach>lambdaQuery()
.eq(Attach::getIsGenerateAiImg, 0)
.eq(Attach::getIsDeleted, 0)
- .eq(Attach::getWayLineJobId, jobId).in(Attach::getResultType, AttachResultTypeEnum.getTheAttachmentType()));
+ .eq(Attach::getPatrolTaskId, jobId).in(Attach::getResultType, AttachResultTypeEnum.getTheAttachmentType()));
}
@Override
@@ -825,4 +779,124 @@
return attachTypeStatisticsVOS;
}
+ /**
+ * 下载附件
+ * @param param 下载参数
+ * @param outputStream 输出流
+ * @return
+ * @throws IOException
+ */
+ @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);
+ if (CollectionUtils.isEmpty(attachList)) {
+ return false;
+ }
+
+ try {
+ // 直接使用传入的输出流创建ZipOutputStream
+ ZipOutputStream zos = new ZipOutputStream(outputStream);
+ // 使用BEST_SPEED而不是BEST_COMPRESSION,提高速度
+ zos.setLevel(Deflater.BEST_SPEED);
+
+ // 使用较小的缓冲区,减少内存使用
+ final int BUFFER_SIZE = 1024 * 1024; // 1MB
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ // 创建Minio客户端
+ MinioClient minioClient = MinioClient.builder()
+ .endpoint(minioPojo.getPath())
+ .credentials(minioPojo.getAccessKey(), minioPojo.getSecretKey())
+ .build();
+
+ // 创建一个Set来跟踪已经使用的文件名,避免重名
+ Set<String> usedFileNames = new HashSet<>();
+
+ // 直接遍历所有附件,不再按任务分组
+ for (AttachVO attachVO : attachList) {
+ String objectName = attachVO.getName();
+ try {
+ // 从MinIO获取文件流
+ InputStream is = minioClient.getObject(
+ GetObjectArgs.builder()
+ .bucket(minioPojo.getBucket())
+ .object(objectName)
+ .build());
+
+ // 提取文件名(不包含路径)
+ String fileName = objectName.substring(objectName.lastIndexOf("/") + 1);
+
+ // 处理文件名冲突
+ 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);
+ } else {
+ uniqueFileName = fileName + "_" + counter;
+ }
+ counter++;
+ }
+
+ // 将文件名添加到已使用集合中
+ usedFileNames.add(uniqueFileName);
+
+ // 创建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();
+ }
+
+ // 关闭资源
+ zos.closeEntry();
+ is.close();
+ } catch (Exception e) {
+ log.error("处理文件 {} 失败: {}", objectName, e.getMessage());
+ }
+ }
+ // 确保所有数据都被写入
+ zos.finish();
+ zos.flush();
+ // 注意:不要关闭ZipOutputStream,因为它会关闭底层的OutputStream
+ // 让调用者负责关闭输出流
+
+ return true;
+
+ } catch (Exception e) {
+ log.error("创建zip文件失败", e);
+ return false;
+ }finally {
+ if (outputStream != null) {
+ try {
+ outputStream.flush();
+ } catch (IOException e) {
+ log.error("Error flushing output stream: {}", e.getMessage());
+ }
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ log.error("Error closing output stream: {}", e.getMessage());
+ }
+ }
+ }
+ }
+
}
--
Gitblit v1.9.3