| | |
| | | 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; |
| | |
| | | * |
| | | * @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); |
| | |
| | | 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; |
| | | |
| | |
| | | log.error("创建zip文件失败", e); |
| | | return false; |
| | | } finally { |
| | | // 步骤10:确保输出流被正确关闭 |
| | | if (outputStream != null) { |
| | | try { |
| | | outputStream.flush(); |