吉安感知网项目-后端
linwei
2026-04-08 4cf4446b163d4322655918bacdaa358e1587cc9c
opt: 下载文件改造
1 files modified
56 ■■■■■ changed files
drone-ops/drone-resource/src/main/java/org/sxkj/resource/service/impl/AttachServiceImpl.java 56 ●●●●● patch | view | raw | blame | history
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;
@@ -788,57 +789,64 @@
     *
     * @param param        下载参数
     * @param outputStream 输出流
     * @return
     * @throws IOException
     * @return 下载是否成功
     * @throws IOException IO异常
     */
    @Override
    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);
@@ -848,33 +856,30 @@
                        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;
@@ -882,6 +887,7 @@
            log.error("创建zip文件失败", e);
            return false;
        } finally {
            // 步骤10:确保输出流被正确关闭
            if (outputStream != null) {
                try {
                    outputStream.flush();