2026-01-24 zwy nginx将大疆机场媒体文件上传minio的请求转发到接口proxyUpload中,由代码做向minio上传,然后模拟minio响应给机场。
2 files added
3 files modified
1 files deleted
243 ■■■■■ changed files
ard-admin/src/main/java/com/ard/web/controller/sample/media/MinioProxyUploadController.java 124 ●●●●● patch | view | raw | blame | history
ard-sample/pom.xml 6 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/storage/service/impl/StorageServiceImpl.java 14 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/utils/MinioStsUtils.java 65 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/utils/S3PutObjectResponse.java 18 ●●●●● patch | view | raw | blame | history
pom.xml 16 ●●●●● patch | view | raw | blame | history
ard-admin/src/main/java/com/ard/web/controller/sample/media/MinioProxyUploadController.java
New file
@@ -0,0 +1,124 @@
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<S3PutObjectResponse> proxyUpload(
            @PathVariable String bucketName,
            @PathVariable String objectName,
            @RequestBody byte[] fileContent,
            @RequestHeader(value = "Content-Type", defaultValue = "application/octet-stream") String contentType
    ) {
        try {
            // 步骤 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()
            );
            // 步骤 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)
            );
            // 步骤 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);
        }
    }
}
ard-sample/pom.xml
@@ -32,6 +32,12 @@
        <javax-jaxb.version>2.3.0</javax-jaxb.version>
    </properties>
    <dependencies>
        <!-- 子模块主动声明,继承主pom中jackson-dataformat-xml的版本,真正引入依赖 -->
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
ard-sample/src/main/java/com/ard/sample/storage/service/impl/StorageServiceImpl.java
@@ -8,7 +8,6 @@
import com.ard.sample.config.DockConfiguration;
import com.ard.sample.storage.service.IStorageService;
import com.ard.oss.service.impl.OssServiceContext;
import com.ard.sample.utils.MinioStsUtils;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.credentials.StaticProvider;
@@ -38,23 +37,10 @@
    @Autowired
    private OssServiceContext ossService;
    @Autowired
    private MinioStsUtils minioStsUtils;
    @Override
    public StsCredentialsDTO getSTSCredentials() {
        CredentialsDTO credentials = ossService.getCredentials();
//        CredentialsDTO credentials = minioStsUtils.getCredentials();
        //验证一下minio的临时凭证是否可用
        log.info("验证http临时凭证是否有效:{}", credentials);
        execMinio(DockConfiguration.endpoint,
                credentials.getAccessKeyId(),
                credentials.getAccessKeySecret(),
                credentials.getSecurityToken(),
                OssConfiguration.region,
                DockConfiguration.bucket,
                "test_object_http");
        log.info("验证https临时凭证是否有效:{}", credentials);
        execMinio(OssConfiguration.endpoint,
ard-sample/src/main/java/com/ard/sample/utils/MinioStsUtils.java
File was deleted
ard-sample/src/main/java/com/ard/sample/utils/S3PutObjectResponse.java
New file
@@ -0,0 +1,18 @@
package com.ard.sample.utils;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 符合 S3 规范的 PutObject 成功响应(XML 格式,大疆可识别)
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@JacksonXmlRootElement(localName = "PutObjectResult") // XML 根节点名称严格遵循 S3 规范
public class S3PutObjectResponse {
    private String ETag; // 文件 MD5 值(带双引号,符合 S3 规范要求)
    private String LastModified; // 上传成功时间(ISO 格式,例:2026-01-23T18:00:00Z)
}
pom.xml
@@ -34,6 +34,8 @@
        <okhttp.version>4.8.1</okhttp.version>
        <mqtt.version>5.5.5</mqtt.version>
        <minio.version>8.3.7</minio.version>
        <commons.version>1.15</commons.version>
        <fasterxml.version>2.13.5</fasterxml.version>
        <forest.version>1.8.0</forest.version>
        <spring-boot-admin.version>2.5.1</spring-boot-admin.version>
    </properties>
@@ -245,6 +247,20 @@
                <version>${minio.version}</version>
            </dependency>
            <!-- 用于构造返回给大疆的 S3 格式 XML 响应 -->
            <dependency>
                <groupId>com.fasterxml.jackson.dataformat</groupId>
                <artifactId>jackson-dataformat-xml</artifactId>
                <version>${fasterxml.version}</version>
            </dependency>
            <!-- 用于计算文件 MD5(生成 ETag,可选但推荐,SDK 8.3.7 不自带该工具类) -->
            <dependency>
                <groupId>commons-codec</groupId>
                <artifactId>commons-codec</artifactId>
                <version>${commons.version}</version>
            </dependency>
            <!-- Forest -->
            <dependency>
                <groupId>com.dtflys.forest</groupId>