zhaowenyu
2026-02-04 9c3c085032bddf6bb5e8b730067292e7f1520328
ard-admin/src/main/java/com/ard/web/controller/sample/media/MinioProxyUploadController.java
@@ -75,22 +75,38 @@
            HttpServletRequest request
    ) {
        log.info("接收参数bucketName:{}",bucketName);
        // ===================== 1. 解析原始路径:保留所有斜杠/末尾分隔符,完全对齐原生MinIO =====================
        String rawUri = request.getRequestURI();
        String requestUri = URLDecoder.decode(rawUri, StandardCharsets.UTF_8);
        // ===================== 核心修复:安全路径解析,替换替代截取,杜绝索引错误 =====================
        // String contextPath = request.getContextPath();
        String rawUri = URLDecoder.decode(request.getRequestURI(), StandardCharsets.UTF_8);
        // 剔除上下文路径,得到纯业务路径
        String purePath = rawUri.substring("/minioTransfer".length());
        String bucketPrefix = "/" + bucketName;
        // 提取原始ObjectKey,无任何修改
        String objectKey = requestUri.substring(bucketPrefix.length());
        log.info("中转MinIO PUT请求 | 桶名:{} | 原始对象路径:{} | 请求体大小:{}byte",
                bucketName, objectKey, rawRequestBody == null ? 0 : rawRequestBody.length);
        log.info("中转MinIO 路径解析开始 | 原始URI:{} | 纯业务路径:{} | 桶前缀:{}", rawUri, purePath, bucketPrefix);
        // 校验路径是否以桶前缀开头
        if (!purePath.startsWith(bucketPrefix)) {
            return buildNativeS3Error("InvalidRequest", "请求路径格式不匹配", rawUri, HttpStatus.BAD_REQUEST);
        }
        // 修复点:replace替换前缀,替代 substring 长度截取,彻底解决索引错位问题
        String objectKey = purePath.replaceFirst("^" + bucketPrefix, "");
        // 兜底处理空路径
        if (objectKey.isEmpty()) {
            objectKey = "/";
        }
        log.info("中转MinIO 路径解析成功 | 纯业务路径:{} | 桶名:{} | 最终对象路径:{}",
                purePath, bucketName, objectKey);
        // byte[] requestData = rawRequestBody == null ? new byte[0] : rawRequestBody;
        try {
            // ===================== 2. 复用原有逻辑:获取临时凭证 =====================
            var credentials = ossService.getCredentials();
            if (credentials == null || credentials.getAccessKeyId() == null || credentials.getSecurityToken() == null) {
                log.error("临时凭证获取失败");
                return buildNativeS3Error("InvalidCredentials", "临时凭证无效", requestUri, HttpStatus.FORBIDDEN);
                return buildNativeS3Error("InvalidCredentials", "临时凭证无效", rawUri, HttpStatus.FORBIDDEN);
            }
            // ===================== 3. 复用原有逻辑:构建MinIO客户端 =====================
@@ -106,7 +122,7 @@
            // 校验桶存在性(兼容原生接口行为,桶不存在返回404)
            boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!bucketExists) {
                return buildNativeS3Error("NoSuchBucket", "指定存储桶不存在", requestUri, HttpStatus.NOT_FOUND);
                return buildNativeS3Error("NoSuchBucket", "指定存储桶不存在", bucketName, HttpStatus.NOT_FOUND);
            }
            // ===================== 4. 透传请求:原样转发大疆原始数据到MinIO,无业务篡改 =====================
@@ -145,10 +161,10 @@
                    .body(nativeError);
        } catch (MinioException e) {
            log.error("MinIO客户端异常:{}", e.getMessage());
            return buildNativeS3Error("ClientError", e.getMessage(), requestUri, HttpStatus.INTERNAL_SERVER_ERROR);
            return buildNativeS3Error("ClientError", e.getMessage(), rawUri, HttpStatus.INTERNAL_SERVER_ERROR);
        } catch (Exception e) {
            log.error("代理中转通用异常:{}", e.getMessage(), e);
            return buildNativeS3Error("InternalError", "服务器代理异常", requestUri, HttpStatus.INTERNAL_SERVER_ERROR);
            return buildNativeS3Error("InternalError", "服务器代理异常", rawUri, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }