快速搭建对象存储服务 - Minio,并解决临时地址暴露ip、短链接请求改变浏览器地址等问题

其他文章

服务容错治理框架resilience4j&sentinel基础应用---微服务的限流/熔断/降级解决方案-CSDN博客

conda管理python环境-CSDN博客

快速搭建对象存储服务 - Minio,并解决临时地址暴露ip、短链接请求改变浏览器地址等问题-CSDN博客

大模型LLMs的MCP入门-CSDN博客

使用LangGraph构建多代理Agent、RAG-CSDN博客

大模型LLMs框架Langchain之链详解_langchain.llms.base.llm详解-CSDN博客

大模型LLMs基于Langchain+FAISS+Ollama/Deepseek/Qwen/OpenAI的RAG检索方法以及优化_faiss ollamaembeddings-CSDN博客

大模型LLM基于PEFT的LoRA微调详细步骤---第二篇:环境及其详细流程篇-CSDN博客

大模型LLM基于PEFT的LoRA微调详细步骤---第一篇:模型下载篇_vocab.json merges.txt资源文件下载-CSDN博客 使用docker-compose安装Redis的主从+哨兵模式_使用docker部署redis 一主一从一哨兵模式 csdn-CSDN博客

docker-compose安装canal并利用rabbitmq同步多个mysql数据_docker-compose canal-CSDN博客

目录

本文要解决的问题

基于Docker-compose的Minio安装

配置文件docker-compose.yml

启动

启动成功后登录webUI

上传资源-web操作

[Java使用 --- 基础版](#Java使用 --- 基础版)

Step1、引入pom依赖

Step2、创建一个新key,并下载

Step3、创建一个application.yml文件

Step4、创建配置实体MinioProperties.java

Step5、创建文件类型枚举类FileType.java

Step6、创建工具类MinioUtils.java

Step7、启动类MinioApplication.java

Step8、测试类---按需

关于Minio真实IP解决方法:

Step1、前提准备

step2、修改application.yml文件

Step3、添加nginx.conf配置,并启动nginx

Step4、生成短链接以及访问短链接

使用重定向访问短链接

[服务端代理请求形式 --- 直接由服务器端发起请求](#服务端代理请求形式 --- 直接由服务器端发起请求)


本文要解决的问题

基础的Minio下载安装、java操作方法、完整的工具类。

使用minio时需要注意的地方:

使用Minio的时候,生成资源的临时访问链接时,生成的地址IP是真实的IP和端口,不安全,怎么办?

生成的Minio的临时访问链接过长怎么办?

从而引导出:

1、如何生成短链接:

2、重定向和转发的区别?

3、重定向的实现方式:

4、如何保证浏览器地址不变的情况下请求资源?

基于Docker-compose的Minio安装

配置文件docker-compose.yml

创建docker-compose.yml,并在docker-compose.yml文件夹下面创建文件夹data

mkdir data

复制代码
version: "3"
services:
  minio:
    image: minio/minio
    container_name: minio
    privileged: true  #是否要获取宿主机的特权
    volumes:
      - /root/common/data/minio/data:/data #目录映射
    ports:
      - "9001:9001"    #端口映射 ---- 不能采用:9001:9000之类的
      - "9000:9000"    #端口映射
    environment:
      MINIO_ACCESS_KEY: xxxxxA1    #账号
      MINIO_SECRET_KEY: xxxxxA2 #密码
    command: server --console-address ':9001' /data  #启动命令
    healthcheck: #健康检测
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

启动

复制代码
docker-compose up -d  # 后台启动

启动成功后登录webUI

URL:ip:9001

Access Key:xxxxxA1

Secret_key:xxxxxA2

上传资源-web操作

创建bucket

上传资源

上传成功后的样式

Java使用 --- 基础版

Step1、引入pom依赖

XML 复制代码
<dependency>
   <groupId>io.minio</groupId>
   <artifactId>minio</artifactId>
   <version>8.5.17</version>
</dependency>

Step2、创建一个新key,并下载

此时系统会下载:credentials.json

{"url":"http://ip:9001/api/v1/service-account-credentials","accessKey":"xxxxx","secretKey":"xxxxxxx","api":"s3v4","path":"auto"}

Step3、创建一个application.yml文件

XML 复制代码
minio:
  url: "http://ip:9000"  # 如果是http://ip:9001 会报错"S3 API Requests must be made to API port."
#  url: "http://www.abcxxxx域名.com"  # 需要配置 指向MinIO真实地址:http://ip:9000; --- 可以使用Nginx反向代理 --- 解决暴露真实IP的方法
  accessKey: "xxxxxA1"
  secretKey: "xxxxxA2"
  api: "s3v4"

Step4、创建配置实体MinioProperties.java

java 复制代码
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "minio")
@Data
public class MinioProperties {
    private String url;
    private String accessKey;
    private String secretKey;
    private String api;
}

Step5、创建文件类型枚举类FileType.java

java 复制代码
package com.lting.minio.model;

import lombok.Getter;
/**
* 枚举类,按需定义
*/
public enum FileType {
    // 修正后的枚举定义(包含扩展名和MIME类型)
    MP4("mp4", "video/mp4"),
    TXT("txt", "text/plain"),
    JSON("json", "application/json"),
    PNG("png", "image/png"),      // 修正PNG类型
    JPG("jpg", "image/jpeg"),
    PDF("pdf", "application/pdf"),
    DOC("doc", "application/msword"),
    DOCX("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
    CSV("csv", "text/csv"),
    EXCEL("xls", "application/vnd.ms-excel");

    private final String extension;
    @Getter
    private final String mimeType;

    // 枚举构造函数
    FileType(String extension, String mimeType) {
        this.extension = extension;
        this.mimeType = mimeType;

    }

    // 根据文件名获取MIME类型
    public static String getMimeTypeForFile(String fileName) {
        String extension = getFileExtension(fileName);
        for (FileType type : values()) {
            if (type.extension.equalsIgnoreCase(extension)) {
                return type.mimeType;
            }
        }

        return "application/octet-stream"; // 默认类型
    }

    // 根据扩展名获取枚举实例
    public static FileType fromExtension(String extension) {
        for (FileType type : values()) {
            if (type.extension.equalsIgnoreCase(extension)) {
                return type;
            }
        }
        throw new IllegalArgumentException("Unsupported file extension: " + extension);
    }

    // 获取文件扩展名辅助方法
    private static String getFileExtension(String fileName) {
        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex == -1 || lastDotIndex == fileName.length() - 1) {
            return "";
        }
        return fileName.substring(lastDotIndex + 1).toLowerCase();
    }
}

Step6、创建工具类MinioUtils.java

java 复制代码
package com.lting.minio;

import com.lting.minio.model.FileType;
import com.lting.minio.model.MinioProperties;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import jakarta.annotation.PostConstruct;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * 0、桶列表
 * 1、CRUD桶Buckets
 * 2、Upload(指定路径)
 * 3、查看桶下面的list列表
 * 4、Delete
 * 5、Download
 */
@Component
@Log4j2
public class MinioUtils {
    @Getter
    private final MinioProperties minioProperties;

    private MinioClient minioClient;

    @Autowired
    public MinioUtils(MinioProperties minioProperties) {
        this.minioProperties = minioProperties;
    }

    @PostConstruct
    public void init() {
        try {
            this.minioClient = MinioClient.builder()
                    .endpoint(minioProperties.getUrl()) // 使用代理域名---不然生成的临时地址为真实ip地址
                    .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
                    .build();
        } catch (Exception e) {
            log.error("初始化minio配置异常", e.fillInStackTrace());
        }
    }

    /**
     * 已经存在的bucket集合列表
     */
    public List<Bucket> listBucket() throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return minioClient.listBuckets();
    }

    /**
     * 创建buckets
     */
    public void createBuckets(final String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 某一个bucket是否存在
     */
    public boolean bucketExists(final String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return this.minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 删除buckets
     */
    public void removeBucket(final String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 上传文件
     * 通用上传方法
     */
    public ObjectWriteResponse uploadFile(final String bucketName, final String fileName, final InputStream inputStream, final long fileSize, String contentType) throws Exception {
        return minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .stream(inputStream, fileSize, -1)
                        .contentType(contentType)
                        .build()
        );
    }

    /**
     * 上传文件(如图片/PDF等)
     */
    public ObjectWriteResponse uploadFile(File file, String bucketName, final String fileName) throws Exception {
        String contentType = Files.probeContentType(file.toPath());
        if (contentType == null) {
            contentType = "application/octet-stream"; // 默认类型
        }

        try (InputStream inputStream = new FileInputStream(file)) {
            return uploadFile(bucketName, fileName, inputStream, file.length(), contentType);
        }
    }

    /**
     * 上传本地文件
     */
    public ObjectWriteResponse uploadLocalFile(final String bucketName, final String filePath, final String fileType) throws Exception {
        this.isMakeBuckets(bucketName);
        File file = new File(filePath);
        try (InputStream inputStream = new FileInputStream(file)) {
            return uploadFile(bucketName, file.getName(), inputStream, file.length(), fileType);
        }
    }

    /**
     * 上传MultipartFile(适用于HTTP文件上传)
     */
    public ObjectWriteResponse uploadMultipartFile(final String bucketName, MultipartFile file, String fileName) throws Exception {
        this.isMakeBuckets(bucketName);
        final String contentType = file.getContentType();
        try (InputStream inputStream = file.getInputStream()) {
            return uploadFile(bucketName, fileName, inputStream, file.getSize(), contentType);
        }
    }

    /**
     * 上传二进制数据(如图片/PDF等) --- 会自动生成文件
     */
    public ObjectWriteResponse uploadBytes(String bucketName, String fileName, byte[] data, final String fileType) throws Exception {
        this.isMakeBuckets(bucketName);
        try (InputStream inputStream = new ByteArrayInputStream(data)) {
            return uploadFile(bucketName, fileName, inputStream, data.length, fileType);
        }
    }

    /**
     * 上传文本内容 --- 会自动生成文件
     */
    public ObjectWriteResponse uploadText(String bucketName, String fileName, String text) throws Exception {
        this.isMakeBuckets(bucketName);
        byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
        try (InputStream inputStream = new ByteArrayInputStream(bytes)) {
            return uploadFile(bucketName, fileName, inputStream, bytes.length, FileType.TXT.getMimeType());
        }
    }

    /**
     * 上传JSON内容 --- 会自动生成文件
     */
    public ObjectWriteResponse uploadJson(final String bucketName, final String fileName, final String json) throws Exception {
        this.isMakeBuckets(bucketName);
        byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
        try (InputStream inputStream = new ByteArrayInputStream(bytes)) {
            return uploadFile(bucketName, fileName, inputStream, bytes.length, FileType.JSON.name());
        }
    }

    private void isMakeBuckets(String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        boolean found = this.bucketExists(bucketName);
        if (!found) {
            this.createBuckets(bucketName);
        }
    }

    /**
     * 查看已经存在的buckets下面的文件
     */
    public List<Result<Item>> listObject(final String bucketName) throws Exception {
        return listObject(bucketName, "", 1000);
    }

    public List<Result<Item>> listObject(final String bucketName, final String prefix, final int size) throws Exception {
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(bucketName)
                .prefix(prefix) // 开始名称
                .maxKeys(size) // 最大数量
                .includeVersions(true)
                .recursive(true) // 是否递归遍历子目录
                .build());
        return StreamSupport.stream(results.spliterator(), false)
                .collect(Collectors.toList());
    }

    /**
     * 下载到流
     */
    public InputStream downloadToStream(String bucketName, String fileName) throws Exception {
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .build()
            );
        } catch (MinioException e) {
            throw new Exception("下载失败: " + e.getMessage());
        }
    }

    /**
     * 下载到本地
     */
    public void downloadToLocal(String bucketName, String fileName, String localFilePath) throws Exception {
        try (InputStream inputStream = downloadToStream(bucketName, fileName)) {
            Path path = Path.of(localFilePath);
            Files.copy(inputStream, path);
        } catch (Exception e) {
            throw new Exception("文件保存失败: " + e.getMessage());
        }
    }

    /**
     * 单个文件删除
     */
    public void deleteObject(String bucketName, String fileName) throws Exception {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .build()
            );
        } catch (MinioException e) {
            throw new Exception("删除失败: " + e.getMessage());
        }
    }

    /**
     * 批量删除(需自行遍历)
     */
    public void batchDelete(String bucketName, List<String> objectNames) {
        objectNames.forEach(name -> {
            try {
                deleteObject(bucketName, name);
            } catch (Exception e) {
                // 记录日志或处理异常
                System.err.println("删除失败: " + name + " | 原因: " + e.getMessage());
            }
        });
    }

    /**
     * 获取预览地址
     * 有效时间默认1H
     *
     * @return
     */
    public String getPreviewFileUrl(String bucketName, String fileName) throws Exception {
        return getPreviewFileUrl(bucketName, fileName, 10, TimeUnit.MINUTES);
    }

    public String getPreviewFileUrl(String bucketName, String fileName, final int expiryTime, final TimeUnit timeUnit) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(fileName)
                        .expiry(expiryTime, timeUnit)
                        .build());
    }
}

Step7、启动类MinioApplication.java

java 复制代码
@SpringBootApplication
@EnableConfigurationProperties(MinioProperties.class) // 显式启用
public class MinioApplication {
    public static void main(String[] args) {
        SpringApplication.run(MinioApplication.class, args);
    }
}

Step8、测试类---按需

java 复制代码
import com.lting.MinioApplication;
import io.minio.ObjectWriteResponse;
import io.minio.Result;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.File;
import java.util.List;

@SpringBootTest(classes = MinioApplication.class)
public class MinioTest {

    @Autowired
    private MinioUtils minioUtils;

    @Test
    @DisplayName("MinioUtils")
    public void properties() {
        System.out.println(minioUtils.getMinioProperties());
    }


    @Test
    @DisplayName("listBuckets")
    public void listBuckets() {
        try {
            List<Bucket> buckets = minioUtils.listBucket();
            buckets.forEach(x -> System.out.println(x.creationDate() + ", " + x.name()));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    @DisplayName("makeBuckets")
    public void createBuckets() {
        try {
            minioUtils.createBuckets("test_code_create");
            listBuckets();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    @DisplayName("deleteBuckets")
    public void deleteBuckets() {
        try {
            minioUtils.removeBucket("test_code_create");
            listBuckets();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    @DisplayName("uploadLocalFile")
    public void uploadLocalFile() throws Exception {
        final String bucketName = "test_code_create";
        final String filePath = "C:\\Users\\xxxxx.png";
        final File file = new File(filePath);
        ObjectWriteResponse owr = minioUtils.uploadFile(file, bucketName,"2025/04/25/pig.png");  // 当在webui中创建的路径:V1/V2/V3一样的效果
        System.out.println(owr.etag());
    }

    @Test
    @DisplayName("listBuckets")
    public void uploadText() throws Exception {
        String content = "{\"url\":\"http://ip:9001/api/v1/service-account-credentials\",\"accessKey\":\"xxxx\",\"secretKey\":\"xxxxxx\",\"api\":\"s3v4\",\"path\":\"auto\"}";
        ObjectWriteResponse owr= minioUtils.uploadText("test_code_create", "/2025/04/25/测试.txt", content);
        System.out.println(owr.etag());
    }

    @Test
    @DisplayName("listObject")
    public void listObject() throws Exception {
        List<Result<Item>> items= minioUtils.listObject("test_code_create");
        items.forEach(x -> {
            try {
                Item item = x.get();
                System.out.println(item.etag() + "\t" + item.objectName() + "\t" + item.size() + "\t" + item.lastModified().toLocalDateTime() + "\t" + item.versionId());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

    @Test
    @DisplayName("getPreviewFileUrl")
    public void getPreviewFileUrl() throws Exception {
        System.out.println(minioUtils.getPreviewFileUrl("test_code_create", "2025/04/25/ltingzx.png"));;
    }

    @Test
    @DisplayName("downloadToLocal")
    public void downloadToLocal() throws Exception {
        minioUtils.downloadToLocal("test_code_create", "requirements.txt", "C:\\Users\\YiYang\\Desktop\\LLama-Factory\\req.txt");
    }

}

关于Minio真实IP解决方法:

使用Minio的时候,生成资源的临时访问链接时,生成的地址IP是真实的IP和端口,不安全,怎么办?

minio通过"minioUtils.getPreviewFileUrl"方法生成临时链接为一般为比较长的链接,比如:"http://127.0.0.1:9001/test/xxxx_47109.png?X-Amz-Algorithm=AWS4-HMAC-SHA256\&X-Amz-Credential=gKK5g5pdjV6LWdW8XtoO%2F20250428%2Fus-east-1%2Fs3%2Faws4_request\&X-Amz-Date=20250428T100736Z\&X-Amz-Expires=600\&X-Amz-SignedHeaders=host\&X-Amz-Signature=bd9560f5655e341263a8944545142449b1a7a393e8952eb20f9be9be9cc1391b"

---- 注意:解决方案有很多种,但是本文使用的是,短链接+Nginx代理+代理请求方案解决

Step1、前提准备

准备一个域名 比如 minio.ltingzx.com

如果没有域名,可以在本地修改hosts文件 添加: 127.0.0.1 minio.ltingzx.com

step2、修改application.yml文件

将url修改为step1中的域名;

java 复制代码
minio:
  url: "http://minio.ltingzx.com"  # 需要配置 指向MinIO真实地址:http://ip:9000; --- 可以使用Nginx反向代理

Step3、添加nginx.conf配置,并启动nginx

关于优化,因为我们使用的是minio存储图片等资源,所以我们可以在Nginx上面开启缓存,用以优化...;

java 复制代码
server {
        listen       80;
        server_name  minio.ltingzx.com;
        # 指定前端项目所在的位置
        location / {
            proxy_pass http://ip:9000; # 指向MinIO真实地址
			proxy_set_header Host $host;
			proxy_set_header X-Real-IP $remote_addr;
			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
			proxy_set_header X-Forwarded-Proto $scheme;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

Step4、生成短链接以及访问短链接

长链接请求接口A ,接口A 生成短链接并返回;

用户拿到短链接后,请求服务的接口,服务接口拿到短链接以后,在映射关系中找到对应的长链接,直接重定向/转发/代理请求即可。

重定向:重定向是需要服务器端返回url地址,由浏览器端再次请求---此时依然会暴露真实ip和端口,且浏览器会改变地址;

转发:之所以不使用转发,核心原因是因为,转发的话需要资源和服务在同一网络---此时我们的minio在云端服务器,服务在本地,所以不适用。

代理请求:拿到短链接后,直接由服务器进行请求;然后服务器拿到的资源返回给response的outputstream流即可。

本文使用UUID临时处理,并且直接使用map方法存储:

真实开发中,我们的映射关系可以存储在数据库(比如Redis/MySQL)中,并且要设置过期时间;

使用重定向访问短链接

java 复制代码
    private final static Map<String, String> URL_MAP = new HashMap<>();
@PostMapping("/generate")
    public R<String> generate(@RequestParam("longUrl") String longUrl) {
        String shortUrl = UUID.randomUUID().toString().replace("-", "");
        URL_MAP.put(shortUrl, longUrl);
        return R.ok(shortUrl);
    }
    /**
     * 短链接点击跳转
     *重定向方法: ---- 要改变浏览器地址
     * 设置sendRedirect,比如response.sendRedirect(URL_MAP.get(shortURL))
     * 或者设置状态302,
     *      response.setStatus(302);
     *      response.setHeader("Location", URL_MAP.get(shortURL));
     * @param shortURL
     */
@GetMapping("/{shortURL}")
    public void redirect(@PathVariable("shortURL") String shortURL, HttpServletResponse response) throws IOException, NotFoundException {
        System.out.println("短链接 redirect to :" + shortURL);
        if (URL_MAP.containsKey(shortURL)) {
//            response.sendRedirect(URL_MAP.get(shortURL));
            response.setStatus(302);
            response.setHeader("Location", URL_MAP.get(shortURL));
//            response.sendRedirect(URL_MAP.get(shortURL));
        } else {
            throw new NotFoundException("短链已过期");
        }
    }

服务端代理请求形式 --- 直接由服务器端发起请求

实现步骤

  1. 生成短链接:将长URL映射为短码(如Base62编码),并存储映射关系。

  2. 处理短链接请求:当访问短链接时,服务器通过短码获取长URL。

  3. 代理请求:服务器端发起HTTP请求到长URL,将响应内容返回客户端,保持地址栏不变。

java 复制代码
    @GetMapping("/{shortURL}")
    public void proxyRequest(@PathVariable("shortURL") String targetUrl, HttpServletResponse resp) throws IOException, NotFoundException {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(URL_MAP.get(targetUrl));
            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
                // 复制状态码
                resp.setStatus(response.getStatusLine().getStatusCode());

                // 复制响应头
                org.apache.http.Header[] headers = response.getAllHeaders();
                for (org.apache.http.Header header : headers) {
                    resp.setHeader(header.getName(), header.getValue());
                }

                // 复制响应内容
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    try (InputStream inputStream = entity.getContent();
                         OutputStream outputStream = resp.getOutputStream()) {
                        byte[] buffer = new byte[1024];
                        int bytesRead;
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                    }
                }
            }
        }
    }
相关推荐
怪我冷i1 小时前
k8s笔记——kubebuilder工作流程
云原生·kubernetes·ai编程·ai写作
小黄人20253 小时前
从零认识阿里云OSS:云原生对象存储的核心价值
阿里云·云原生·云计算
zhousenshan9 小时前
AAA GitOps详解
云原生
风屿Wind1 天前
从此,K8S入门0门槛!
云原生·容器·kubernetes
玄明Hanko1 天前
Java云原生+quarkus
java·开发语言·云原生·quarkus
weisian1511 天前
云原生--核心组件-容器篇-5-Docker核心之-容器
docker·云原生
爱幻想-hjyp1 天前
Mac M1安装 Docker Desktop 后启动没反应
macos·docker·eureka
一个向上的运维者1 天前
Kubernetes(k8s)的API Server 组件原理与结合生产实战教程
云原生·容器·kubernetes
cooldream20092 天前
深入理解虚拟机与容器:原理、对比与应用场景分析
云原生·系统架构师