package com.ard.web.controller.sample.media; import com.ard.common.config.OssConfiguration; import com.ard.oss.model.CredentialsDTO; import com.ard.oss.service.impl.OssServiceContext; import com.ard.sample.utils.S3PutObjectResponse; import io.minio.MinioClient; import io.minio.PutObjectArgs; import io.minio.credentials.StaticProvider; import io.minio.errors.MinioException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.ByteArrayInputStream; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; /** * 适配 MinIO SDK 8.3.7 + 服务器 RELEASE.2024-08-03 * 承接大疆 PUT 上传请求,中转上传到 MinIO HTTPS 接口 */ @Slf4j @RestController @RequestMapping("/minioTransfer") // 根路径,匹配 Nginx 转发的 /dock/、/kmz/ 路径 public class MinioProxyUploadController { // @Autowired // private MinioStsUtils minioStsUtils; // 复用你已有的 HTTPS 临时凭证工具类 @Autowired private OssServiceContext ossService; // 你的 MinIO HTTPS 原生地址(固定,与服务器版本匹配) // private static final String MINIO_HTTPS_ENDPOINT = "https://192.168.1.3:9001"; /** * 核心接口:中转大疆的 PUT 上传请求到 MinIO * 匹配路径:/{bucketName}/{objectName}(例:/dock/xxx.png) * @param bucketName 桶名(dock/kmz,与大疆上传一致) * @param objectName 文件名(例:dji-test-20240124.png) * @param fileContent 文件二进制内容(大疆上传的核心数据) * @param contentType 文件 MIME 类型(默认二进制流,适配多种文件格式) * @return S3 规范 XML 响应(返回给大疆,衔接后续流程) */ @PutMapping("/{bucketName}/{objectName}") public ResponseEntity proxyUpload( @PathVariable String bucketName, @PathVariable String objectName, @RequestBody byte[] fileContent, @RequestHeader(value = "Content-Type", defaultValue = "application/octet-stream") String contentType ) { try { log.info("开始中转上传文件到 MinIO,收到参数:bucketName={}, objectName={}",bucketName,objectName); // 步骤 1:获取 MinIO HTTPS 临时凭证(复用你的已有工具类,已验证可行) CredentialsDTO credentials = ossService.getCredentials(); if (credentials == null || credentials.getAccessKeyId() == null || credentials.getSecurityToken() == null) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); } // 步骤 2:构建 MinioClient(适配 SDK 8.3.7,忽略私有 SSL 证书,使用临时凭证) MinioClient minioClient = MinioClient.builder() .endpoint(OssConfiguration.endpoint) // 指定 MinIO HTTPS 原生地址 .credentialsProvider(new StaticProvider( credentials.getAccessKeyId(), credentials.getAccessKeySecret(), credentials.getSecurityToken() // SDK 8.3.7 支持临时凭证的 sessionToken 参数 )) .build(); // 步骤 3:中转上传文件到 MinIO(核心:SDK 8.3.7 兼容 PutObjectArgs 构建) minioClient.putObject( PutObjectArgs.builder() .bucket(bucketName) // 桶名与大疆上传一致(dock/kmz) .object(objectName) // 文件名与大疆上传一致 .stream( new ByteArrayInputStream(fileContent), // 文件二进制流 fileContent.length, // 文件字节长度 -1 // 自动分块(SDK 8.3.7 支持 -1 自动处理,适配小文件上传) ) .contentType(contentType) // 文件 MIME 类型 .build() ); log.info("文件上传成功,文件名:{}",objectName); // 步骤 4:构造符合 S3 规范的成功响应(返回给大疆,确保流程衔接) S3PutObjectResponse successResponse = new S3PutObjectResponse( // ETag:生成文件 MD5 值(带双引号,符合 S3 规范,依赖 commons-codec) "\"" + org.apache.commons.codec.digest.DigestUtils.md5Hex(fileContent) + "\"", // LastModified:当前时间(ISO 格式,MinIO 服务器兼容该格式) OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) ); log.info("构造回复大疆机场的响应信息-S3PutObjectResponse:{}",successResponse); // 步骤 5:返回 200 OK + XML 格式响应(Spring Boot 自动转换实体类为 XML) return ResponseEntity.status(HttpStatus.OK) .contentType(MediaType.APPLICATION_XML) .body(successResponse); } catch (MinioException e) { // MinIO 上传异常(SDK 8.3.7 抛出的异常类型,与高版本一致) e.printStackTrace(); log.info("MinIO 上传异常:{}",e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); } catch (NoSuchAlgorithmException | InvalidKeyException e) { // 凭证/算法异常(适配 SDK 8.3.7 的加密校验逻辑) e.printStackTrace(); log.info("MinIO 凭证或算法异常:{}",e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); } catch (Exception e) { // 其他通用异常 e.printStackTrace(); log.info("MinIO 异常:{}",e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); } } }