zhaowenyu
2026-02-05 08d5077ac4139455e6573f3a61bbae99ca62573e
2026-02-04 优化minio上传,兼容minio所有接口的转发
1 files added
1 files deleted
247 ■■■■■ changed files
ard-admin/src/main/java/com/ard/web/controller/sample/media/MinioGeneralProxyController.java 118 ●●●●● patch | view | raw | blame | history
ard-admin/src/main/java/com/ard/web/controller/sample/media/MinioStreamProxyController.java 129 ●●●●● patch | view | raw | blame | history
ard-admin/src/main/java/com/ard/web/controller/sample/media/MinioGeneralProxyController.java
File was deleted
ard-admin/src/main/java/com/ard/web/controller/sample/media/MinioStreamProxyController.java
New file
@@ -0,0 +1,129 @@
package com.ard.web.controller.sample.media;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
import java.util.Enumeration;
/**
 * MinIO S3协议 原生流式透传代理
 * 零修改转发请求头、请求体、响应,保证S3签名校验通过
 */
@Api(tags = "机场上传minio")
@Slf4j
@RestController
@RequestMapping("/minioTransfer")
public class MinioStreamProxyController {
    // 配置MinIO服务地址,例如:http://127.0.0.1:9000
    @Value("${oss.endpoint}")
    private String minioEndpoint;
    private static final String PROXY_PREFIX = "/minioTransfer";
    // 流拷贝缓冲区大小(适配大文件)
    private static final int BUFFER_SIZE = 8192;
    @ApiOperation("MinIO原生接口代理(无感中转)")
    @RequestMapping("/**")
    public void streamProxy(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1. 解析原始请求信息
        String method = request.getMethod();
        String requestUri = request.getRequestURI();
        String queryString = request.getQueryString();
        log.info("S3代理透传 | 方法:{} | 路径:{} | 参数:{}", method, requestUri, queryString);
        // 2. 拼接MinIO目标地址
        String targetPath = requestUri.substring(PROXY_PREFIX.length());
        StringBuilder targetUrl = new StringBuilder(minioEndpoint).append(targetPath);
        if (queryString != null && !queryString.isBlank()) {
            targetUrl.append("?").append(queryString);
        }
        // 3. 创建目标连接
        URL url = new URL(targetUrl.toString());
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        // 配置连接参数
        conn.setRequestMethod(method);
        conn.setDoInput(true);
        conn.setConnectTimeout(10000);
        conn.setReadTimeout(300000);
        // 关键:允许写入请求体(POST/PUT/PATCH)
        conn.setDoOutput(!method.equalsIgnoreCase("GET") && !method.equalsIgnoreCase("HEAD"));
        // 禁止自动重定向,保持S3协议一致性
        conn.setInstanceFollowRedirects(false);
        try {
            // 4. 透传所有请求头(S3签名头完整保留,不修改、不删除)
            Enumeration<String> headerNames = request.getHeaderNames();
            if (headerNames != null) {
                while (headerNames.hasMoreElements()) {
                    String name = headerNames.nextElement();
                    String value = request.getHeader(name);
                    conn.setRequestProperty(name, value);
                }
            }
            // 5. 透传请求体(流式拷贝,不加载到内存,保证签名完整性)
            if (conn.getDoOutput()) {
                try (InputStream requestIn = request.getInputStream();
                     OutputStream connOut = conn.getOutputStream()) {
                    byte[] buffer = new byte[BUFFER_SIZE];
                    int len;
                    while ((len = requestIn.read(buffer)) != -1) {
                        connOut.write(buffer, 0, len);
                    }
                }
            }
            // 6. 获取MinIO响应,透传状态码、响应头、响应体
            int statusCode = conn.getResponseCode();
            response.setStatus(statusCode);
            // 透传响应头
            conn.getHeaderFields().forEach((key, values) -> {
                if (key != null && values != null && !values.isEmpty()) {
                    response.setHeader(key, values.get(0));
                }
            });
            // 透传响应流(区分正常流/错误流)
            InputStream responseIn;
            try {
                responseIn = conn.getInputStream();
            } catch (IOException e) {
                responseIn = conn.getErrorStream();
            }
            try (OutputStream responseOut = response.getOutputStream()) {
                byte[] buffer = new byte[BUFFER_SIZE];
                int len;
                while ((len = responseIn.read(buffer)) != -1) {
                    responseOut.write(buffer, 0, len);
                }
            } finally {
                if (responseIn != null) {
                    responseIn.close();
                }
            }
            log.info("S3代理转发成功 | 目标地址:{} | 状态码:{}", targetUrl, statusCode);
        } catch (Exception e) {
            log.error("S3代理转发异常", e);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "代理服务异常");
        } finally {
            // 关闭连接
            conn.disconnect();
        }
    }
}