基于 RustFS 与 Spring Boot 构建高性能 S3 文件服务

2026-09-14T22:52:26+08:00 | 13分钟阅读

Mizar
基于 RustFS 与 Spring Boot 构建高性能 S3 文件服务

基于 RustFS 与 Spring Boot 构建高性能 S3 文件服务

简介

RustFS 是一款用 Rust 构建的高性能分布式对象存储系统。RustFS将MinIO的简洁性与Rust的内存安全和原始性能相结合。兼容AWS S3 协议,完全开源,并针对数据湖、人工智能和大数据工作负载进行了优化。

与其他存储系统不同,RustFS 采用允许的 Apache 2.0 许可证发布,避免了 AGPL 的限制。以Rust为基础,RustFS为下一代对象存储提供了更优越的速度和安全的分布式功能。

主要特性

  • 高性能:基于Rust构建,确保最大速度和资源效率。
  • 分布式架构:可扩展且容错的设计,适合大规模部署。
  • AWS S3兼容性:与常见的S3兼容应用和工具无缝集成;当前覆盖范围通过S3兼容性矩阵跟踪。
  • OpenStack Swift API:原生支持带有Keystone认证的Swift协议。
  • OpenStack Keystone 集成:原生支持 OpenStack Keystone 认证,支持 X-Auth-Token 头部。
  • 数据湖支持:针对高吞吐量大数据和AI工作负载进行了优化。
  • 开源:采用Apache 2.0许可,鼓励社区无限制贡献和商业使用。
  • 用户友好:具有可视化管理控制台,方使管理。

状态说明:✅可用——已发货并由CI门覆盖;🧪 预览——以选择加入标志或有限制兼容性声明发布。

FeatureStatusFeatureStatus
S3 Core Features✅ AvailableDistributed Mode✅ Available
Upload / Download✅ AvailableSingle Node Mode✅ Available
Versioning✅ AvailableBitrot Protection✅ Available
Object Lock (WORM)✅ AvailableHealing & Scanner✅ Available
Server-Side Encryption✅ AvailablePool Expansion / Decommission✅ Available
RustFS KMS✅ AvailableBucket Replication✅ Available
Lifecycle Management (ILM)✅ AvailableSite Replication✅ Available
ILM Tiering (Remote S3)✅ AvailableBucket Quota✅ Available
S3 Select✅ AvailableEvent Notifications✅ Available
S3 Tables (Iceberg REST)🧪 PreviewAudit Logging✅ Available
IAM / Policies✅ AvailableLogging & Observability✅ Available
OIDC / SSO✅ AvailableWeb Console✅ Available
Keystone Auth✅ AvailableK8s Helm Charts✅ Available
Swift API✅ AvailableFTPS / WebDAV✅ Available
Multi-Tenancy✅ AvailableSFTP✅ Available
MinIO On-Disk Compatibility🧪 Preview

注释:

  • RustFS KMS:支持Vault(KV2 / Transit)和AWS KMS后端用于生产环境。和后端仅用于开发和测试。参见KMS后端安全属性Local Static
  • Swift API / SFTP:可选的货物功能(、 、 或 )。默认版本中启用了FTPS和WebDAV。--features swift --features sftp full
  • S3 表格:以 Iceberg REST 目录形式发布,自动覆盖 PyIceberg 和 DuckDB;其他引擎和厂商配置文件在S3表支持矩阵中列出了有界的声明。
  • MinIO 磁盘兼容性:功能后面有限制,不包含在默认版本中。MinIO加密的对象在RustFS中无法读取。参见 MinIO 文件格式互操作性rio-v2

RustFS vs MinIO 性能

压力测试环境:

TypeParameterRemark
CPU2 CoreIntel Xeon (Sapphire Rapids) Platinum 8475B, 2.7/3.2 GHz
Memory4GB
Network15Gbps
Drive40GB x 4IOPS 3800 / Drive

RustFS vs Other Object Storage

FeatureRustFSOther Object Storage
Console ExperiencePowerful Console Comprehensive management interface.Basic / Limited Console Often overly simple or lacking critical features.
Language & SafetyRust-based Memory safety by design.Go or C-based Potential for memory GC pauses or leaks.
Data SovereigntyNo Telemetry / Full Compliance Guards against unauthorized cross-border data egress. Compliant with GDPR (EU/UK), CCPA (US), and APPI (Japan).Potential Risk Possible legal exposure and unwanted data telemetry.
LicensingPermissive Apache 2.0 Business-friendly, no “poison pill” clauses.Restrictive AGPL v3 Risk of license traps and intellectual property pollution.
CompatibilityS3-Compatible Core Works with common S3-compatible clients, with coverage tracked in the compatibility matrix.Variable Compatibility May lack support for local cloud vendors or specific APIs.
Edge & IoTStrong Edge Support Ideal for secure, innovative edge devices.Weak Edge Support Often too heavy for edge gateways.
Risk ProfileEnterprise Risk Mitigation Clear IP rights and safe for commercial use.Legal Risks Intellectual property ambiguity and usage restrictions.

安装部署

本文是部署的单服务器生产环境,基于docker在服务器上安装。

前提条件

  • 一台配备 systemd 的 Linux 服务器(x86_64 或 aarch64),以及 root 或 sudo 权限
  • 已安装 unzip,并可访问外部网络以下载软件包
  • 你的计算机可访问端口 9000(S3 API)和 9001(控制台)
  • 工作站上已安装 rc,用于命令行验证流程

安装并启动

运行官方安装脚本:

curl -O https://rustfs.com/install_rustfs.sh && bash install_rustfs.sh

该脚本会将二进制文件安装到 /usr/local/bin/rustfs,注册 rustfs systemd 服务并启动它。默认情况下,数据存储在 /data/rustfs0,服务监听端口 9000(S3 API)和 9001(控制台);安装期间可以调整数据路径和端口。成功后会输出类似以下摘要:

RustFS has been installed and started successfully!Service port: 9000,  Console port: 9001,  Data directory: /data/rustfs0[SECURITY WARNING] Please change the default value for RUSTFS_ACCESS_KEY/RUSTFS_SECRET_KEY immediately ...  Config file: /etc/default/rustfs

设置凭证

  1. /etc/default/rustfs中设置:

    [root@hcss-ecs-9434 ~]# vim /etc/default/rustfs 
    
    RUSTFS_ACCESS_KEY=<your-access-key>
    RUSTFS_SECRET_KEY=<your-secret-key>   ; e.g. output of: openssl rand -base64 24
    

    image-20260914211209426

    注意:

    1. 访问密钥请仅使用大写字母和数字。请勿直接将未经转换的 Base64 输出用于 RUSTFS_ACCESS_KEY,因为 Base64 可能包含 /,与 AWS Signature Version 4 的凭证作用域分隔符冲突。
    2. 不要保留默认凭证。如果未设置 RUSTFS_ACCESS_KEY / RUSTFS_SECRET_KEY,服务器会回退到内置默认值 rustfsadmin / rustfsadmin。该值只能用于一次性本地测试,绝不能用于他人可访问的环境。
  2. Esc,输入 :wq 然后回车。

  3. 重启服务使配置生效 修改完配置后,需要重启 RustFS 服务:

    [root@hcss-ecs-9434 ~]# sudo systemctl restart rustfs
    
  4. 检查服务状态

    确认服务是否正常运行:

    [root@hcss-ecs-9434 ~]# sudo systemctl status rustfs --no-pager   # should report: active (running)
    
  5. 如果看到绿色的 active (running) 字样,就说明一切正常了。

    image-20260914211356409

登录控制台

在浏览器中打开 http://<server-ip>:9001,使用访问密钥RUSTFS_ACCESS_KEY和秘密密钥登录RUSTFS_SECRET_KEY

image-20260914210515045

提示:如果你的服务器有防火墙或云服务商的安全组,请确保放行了 90009001 这两个 TCP 端口,否则浏览器可能无法打开页面。

创建存储桶并上传文件

  1. 在控制台首页选择 创建存储桶,输入名称(例如 my-bucket)并确认。

    image-20260914210717373

    image-20260914210744345

  2. 打开存储桶,使用上传操作添加任意本地文件。

    image-20260914210829057

    image-20260914210841711

  3. 单击上传的对象查看详情。此时对象存储已可正常使用。

    image-20260914210913830

扩展

浏览器打不开页面(无法访问)

通常是防火墙云服务商安全组拦截了端口。请检查以下两点:

  • 云服务器安全组:登录你的云服务商控制台(如阿里云、腾讯云、华为云等),在安全组/防火墙规则中,检查是否放行了 TCP 9000 和 9001 端口。
  • 系统防火墙:在服务器上是否开启了 firewalldufw
云服务器安全组放行TCP 9000 和 9001端口

云服务器安全组:登录你的云服务商控制台(如阿里云、腾讯云、华为云等),在安全组/防火墙规则中,放行 TCP 9000 和 9001 端口。

img

具体步骤:

  1. 选择“入方向规则”

  2. 点击左侧的“添加规则”按钮(在“一键放通常用端口”旁边)。

  3. 在弹出的窗口中,按照以下参数填写(以华为云为例,大同小异):

    • 优先级:填写**1**即可。
    • 类型:选择 自定义 TCP(或者直接选 TCP)。
    • 协议端口
      • 如果只想放行 RustFS 所需的端口,填入:9000,9001 (有的控制台可能需要分开填,比如填两行,一行 9000,一行 9001)。
      • 如果觉得麻烦,或者以后还要部署其他服务,也可以直接选“一键放通常用端口”,但那会开放很多端口,不够安全。建议自定义放行 9000,9001
    • 源地址
      • 推荐:默认填 0.0.0.0/0。这代表允许互联网上任何 IP 访问你的这两个端口(方便回家、在公司等不同网络访问)。
      • 更安全(可选):如果你有固定的办公或家庭公网 IP,可以只填自己的 IP 地址(例如 123.123.123.123/32),这样只有自己能连,最安全。
    • 描述:可以填“RustFS 服务与控制台”,方便以后辨认。
    • 策略:必须是 允许
  4. 点击“保存”或“确定”。添加完规则后,不需要重启服务器,安全组规则通常会立即生效。

系统防火墙放行TCP 9000 和 9001 端口

系统防火墙:如果在服务器上开启了 firewalldufw,也需要放行这两个端口。例如在 CentOS 上可以执行:

sudo firewall-cmd --zone=public --add-port=9000/tcp --permanent
sudo firewall-cmd --zone=public --add-port=9001/tcp --permanent
sudo firewall-cmd --reload

自定义域名访问 RustFS

在宝塔面板中设置自定义域名访问 RustFS,核心是使用反向代理功能,将自定义域名指向服务器上运行 RustFS 的服务监听端口 9000()和 9001(控制台)。

Rustfs控制台

具体步骤:

  1. 添加站点:进入宝塔面板的 网站添加站点,填写你的域名(如 console.你的域名.com),PHP 版本选择“纯静态”即可,提交后会生成一个用于反向代理的空站点。

    image-20260914214141171

    image-20260914214154242

  2. 配置反向代理:在网站列表找到该站点,点击 设置反向代理添加反向代理

    • 代理名称:自定义(如 RustFS-Console和)。

    • 目标 URL:填写 http://127.0.0.1:9001(即 RustFS 控制台的本机地址)。

    • 发送域名:保持默认的 $http_host 即可,一般无需修改。

    • 开启缓存:关闭**“开启缓存”** (变成灰色)。

      原因:RustFS 是用来做对象存储的,文件传输本身不需要 Nginx 再做一层缓存,开启反而容易导致大文件传输出错 或占用大量磁盘。

      image-20260914214400054

      image-20260914214342732

  3. 保存并测试:保存配置后,通过浏览器访问你设置的自定义域名,确认能否正常进入 RustFS 登录界面。

    image-20260914214611523

Rustfs S3 API

具体步骤:

  1. 添加站点:进入宝塔面板的 网站添加站点,填写你的域名(如 console.你的域名.com),PHP 版本选择“纯静态”即可,提交后会生成一个用于反向代理的空站点。

    image-20260914214141171

    image-20260914214154242

  2. 配置反向代理:在网站列表找到该站点,点击 设置反向代理添加反向代理

    • 代理名称:自定义(如 RustFS-Console和)。

    • 目标 URL:填写 http://127.0.0.1:9001(即 RustFS 控制台的本机地址)。

    • 发送域名:保持默认的 $http_host 即可,一般无需修改。

    • 开启缓存:关闭**“开启缓存”** (变成灰色)。

      原因:RustFS 是用来做对象存储的,文件传输本身不需要 Nginx 再做一层缓存,开启反而容易导致大文件传输出错 或占用大量磁盘。

      image-20260914214400054

      image-20260914214822497

  3. 保存并测试:保存配置后,通过浏览器访问你设置的自定义域名,确认能否正常进入 RustFS 登录界面。

    image-20260914214611523

补充与建议

重要补充(RustFS 特殊配置)

由于 RustFS 是对象存储服务,且之前安装了 systemd 服务,建议给 Nginx 配置添加特殊指令,以避免访问存储桶时出现 Bucket not found 错误。

根据社区反馈,需要在 Nginx 配置文件中添加 proxy_cache_convert_head off; 指令,否则存储桶访问会报错。

具体步骤:

在Rustfs控制台网站的反向代理中,点击配置文件,编辑配置文件,添加proxy_cache_convert_head off

image-20260914215124668

image-20260914215059449

安全建议

由于 RustFS 控制台涉及管理操作,在配置好域名后,建议启用 HTTPS(在站点设置中部署 SSL 证书),并在 Nginx 中限制访问来源 IP,或通过 VPN/内网访问,避免控制台直接暴露在公网

Java SDK 集成 Rustfs

简单Maven项目

设置Maven项目

创建Maven项目

创建一个新的 Maven 项目:

spring-rustfs/
├── pom.xml
└── src/
    └── main/
        └── java/
            └── com/
                └── amoon/
    				└── springrustfs/
                    	└── RustfsS3Example.java

image-20260914220514253

添加依赖项

pom.xml 中添加 AWS SDK 依赖项:

<dependencies>
  <dependency>
   <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.25.27</version>
  </dependency>
</dependencies>

连接到RustFS

创建以下类,直接编译和运行。

package com.amoon.springrustfs;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import java.net.URI;
import java.nio.file.Paths;
/**
 * @ClassName: RustfsS3Example
 * @Description: TODO
 * @Author: ZM
 * @Date: 2026年09月12日  21:37
 **/
public class RustfsS3Example {
    public static void main(String[] args) {
        // 1. Initialize S3 client
        S3Client s3 = S3Client.builder()
                .endpointOverride(URI.create("https://rustfsapi.zhangmeng.me")) // RustFS S3 API address
                .region(Region.US_EAST_1) // RustFS default region
                .credentialsProvider(
                        StaticCredentialsProvider.create(
                                AwsBasicCredentials.create("<your-access-key>", "<your-secret-key>")
                        )
                )
                // RustFS uses path-style URLs by default; virtual-host style requires RUSTFS_SERVER_DOMAINS
                .forcePathStyle(true)
                .build();
        // 2. Create bucket
        String bucket = "example-bucket";
        try {
            s3.createBucket(CreateBucketRequest.builder().bucket(bucket).build());
            System.out.println("Bucket created: " + bucket);
        } catch (BucketAlreadyExistsException | BucketAlreadyOwnedByYouException e) {
            System.out.println("Bucket already exists.");
        }
        // 3. Upload file
        s3.putObject(
                PutObjectRequest.builder().bucket(bucket).key("hello.txt").build(),
                Paths.get("/path/to/hello.txt")
        );
        System.out.println("Uploaded hello.txt");
        // 4. Download file
        s3.getObject(
                GetObjectRequest.builder().bucket(bucket).key("hello.txt").build(),
                Paths.get("/path/to/downloaded-hello.txt")
        );
        System.out.println("Downloaded hello.txt");
        // 5. List objects
        ListObjectsV2Response listResponse = s3.listObjectsV2(ListObjectsV2Request.builder().bucket(bucket).build());
        listResponse.contents().forEach(obj -> System.out.println("Found object: " + obj.key()));
        // 6. Delete object
        s3.deleteObject(DeleteObjectRequest.builder().bucket(bucket).key("hello.txt").build());
        System.out.println("Deleted hello.txt");
        // 7. Delete bucket (optional)
        // s3.deleteBucket(DeleteBucketRequest.builder().bucket(bucket).build());
    }
}

预期输出:

Bucket created: my-bucket
Uploaded hello.txt
Downloaded hello.txt
Found object: hello.txt
Deleted hello.txt

常见问题与故障排除

问题原因解决方案
S3Exception: 301 Moved Permanently未启用路径风格或区域错误设置 .forcePathStyle(true) 并使用区域 us-east-1
ConnectException: Connection refusedRustFS 未启动或端口不正确检查 RustFS 状态和端口
403 ForbiddenAccessKey / SecretKey 错误检查身份验证配置
上传失败且无响应SDK 默认使用 HTTPS,而 RustFS 仅支持 HTTP(或需要证书)使用 http:// 地址并配置 endpointOverride

Spring Boot项目

结合Spring Boot实现文件上传、下载、删除。

创建项目

image-20260914221737002

image-20260914221758364

pom依赖

pom.xml 中添加 AWS SDK 依赖项:

<dependencies>
  <dependency>
   <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.25.27</version>
  </dependency>
</dependencies>

完整pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.1.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.amoon</groupId>
    <artifactId>spring-rustfs</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-rustfs</name>
    <description>spring-rustfs</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webmvc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webmvc-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- AWS S3 Java SDK相依赖 -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
            <version>2.25.27</version>
        </dependency>
        <!-- Swagger接口文档 相关依赖 -->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.8.6</version>
        </dependency>
        <!-- hutool 工具类 相关依赖 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.38</version>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <annotationProcessorPaths>
                                <path>
                                    <groupId>org.projectlombok</groupId>
                                    <artifactId>lombok</artifactId>
                                </path>
                                <path>
                                    <groupId>org.springframework.boot</groupId>
                                    <artifactId>spring-boot-configuration-processor</artifactId>
                                </path>
                            </annotationProcessorPaths>
                        </configuration>
                    </execution>
                    <execution>
                        <id>default-testCompile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                        <configuration>
                            <annotationProcessorPaths>
                                <path>
                                    <groupId>org.projectlombok</groupId>
                                    <artifactId>lombok</artifactId>
                                </path>
                            </annotationProcessorPaths>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

连接配置信息

添加RustFS连接配置信息。

resourceapplication.yml中添加如下配置信息:

application.yml

server:
  port: 8080

spring:
 application:
    name: spring-rustfs

rustfs:
  # RustFS S3 API address
  endpoint: https://rustfsapi.zhangmeng.me
  bucketName: simple
  accessKey: your-access-key
  secretKey: your-secret-key

配置类

创建config文件夹,并创建RustFSConfig.javaBucketPolicyConfig.java两个配置类。

image-20260914222608723

RustFSConfig.java

创建RustFSConfig配置类,用于读取配置文件中的连接配置,然后实例化S33客户端对应的Bean。

package com.amoon.springrustfs.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

import java.net.URI;

/**
 * @ClassName: RustFSConfig
 * @Description: 初始化RustFS配置
 * @Author: ZM
 * @Date: 2026年09月12日  22:23
 **/
@Configuration
public class RustFSConfig {

    @Value("${rustfs.endpoint}")
    private String ENDPOINT;
    @Value("${rustfs.accessKey}")
    private String ACCESS_KEY;
    @Value("${rustfs.secretKey}")
    private String SECRET_KEY;

    @Bean
    public S3Client s3Client() {
        //初始化 S3 客户端
        return S3Client.builder()
                //RustFS地址
                .endpointOverride(URI.create(ENDPOINT))
                //可写死,RustFS 不校验 region
                .region(Region.US_EAST_1)
                .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(ACCESS_KEY, SECRET_KEY)))
                //关键配置! RustFS需启用Path-Style
                .forcePathStyle(true)
                .build();
    }
}
BucketPolicyConfig.java

创建存储桶的访问策略,设置为只读权限

package com.amoon.springrustfs.config;

import cn.hutool.core.collection.CollUtil;
import com.amoon.springrustfs.dto.BucketPolicyConfigDto;

import java.util.List;

/**
 * @ClassName: BucketPolicyConfig
 * @Description: 创建存储桶的访问策略,设置为只读权限
 * @Author: ZM
 * @Date: 2026年09月13日  1:15
 **/
public class BucketPolicyConfig {

    /**
     * 创建存储桶的访问策略,设置为只读权限
     * @param bucketName
     * @return
     */
    public static BucketPolicyConfigDto createBucketPolicyConfigDto(String bucketName) {
        BucketPolicyConfigDto.Statement statement = BucketPolicyConfigDto.Statement.builder()
                .Effect("Allow")
                .Principal(BucketPolicyConfigDto.Principal.builder().AWS(List.of(new String[]{"*"})).build())
                .Action(List.of(new String[]{"s3:GetObject"}))
                .Resource(List.of(new String[]{"arn:aws:s3:::" + bucketName + "/*"}))
                .build();
        return BucketPolicyConfigDto.builder()
                .Version("2012-10-17")
                .Statement(CollUtil.toList(statement))
                .build();
    }

}

通用类

创建common.api文件夹,并创建CommonResult.javaRustFSUploadResult.java两个通用类。

CommonResult.java

创建CommonResult类,用于统一接口返回格式。

CommonResult.java

package com.amoon.springrustfs.common.api;

import lombok.Data;

/**
 * @ClassName: CommonResult
 * @Description: 统一接口返回格式
 * @Author: ZM
 * @Date: 2026年09月12日  22:58
 **/
@Data
public class CommonResult<T> {

    private Integer code;
    private String message;
    private T data;

    public CommonResult() {
    }

    public CommonResult(Integer code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    // 快捷成功方法
    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<>(200, "操作成功", data);
    }

    // 快捷失败方法
    public static <T> CommonResult<T> error(String message) {
        return new CommonResult<>(500, message, null);
    }

}
CommonResult.java

创建CommonResult类,用于RustFS文件上传返回结果。

CommonResult.java

package com.amoon.springrustfs.common.api;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

/**
 * @ClassName: RustFSUploadResult
 * @Description: RustFS文件上传返回结果
 * @Author: ZM
 * @Date: 2026年09月12日  23:55
 **/
@Data
public class RustFSUploadResult {
    @Schema(title = "文件访问URL")
    private String url;
    @Schema(title = "文件名称")
    private String name;
}

实体类

创建dto文件夹,并创建BucketPolicyConfigDto.java实体类,RustFS 存储桶策略配置 DTO。

BucketPolicyConfigDto.java

package com.amoon.springrustfs.dto;

/**
 * @ClassName: BucketPolicyConfigDto
 * @Description: RustFS 存储桶策略配置 DTO,
 * @Author: ZM
 * @Date: 2026年09月12日  23:26
 * 对应标准的 S3 Bucket Policy JSON 结构
 * {
 *   "Version": "2012-10-17",
 *   "Statement": [
 *     {
 *       "Sid": "",
 *       "Effect": "Allow",
 *       "Principal": {
 *         "AWS": ["*"]
 *       },
 *       "Action": ["s3:GetObject"],
 *       "Resource": ["arn:aws:s3:::image-hosting/*"]
 *     }
 *   ]
 * }
 **/
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Data;

import java.util.List;

@Data
@Builder
public class BucketPolicyConfigDto {

    /**
     * 策略版本,通常为 "2012-10-17"
     */
    @JsonProperty("Version")
    private String Version;

    /**
     * 策略语句列表
     */
    @JsonProperty("Statement")
    private List<Statement> Statement;

    @Data
    @Builder
    public static class Statement {
        /**
         * 语句 ID,可选
         */
        @JsonProperty("Sid")
        private String Sid;

        /**
         * 效果:Allow 或 Deny
         */
        @JsonProperty("Effect")
        private String Effect;

        /**
         * 主体(被授权者)
         */
        @JsonProperty("Principal")
        private Principal Principal;

        /**
         * 允许或拒绝的操作列表
         */
        @JsonProperty("Action")
        private List<String> Action;

        /**
         * 资源 ARN 列表
         */
        @JsonProperty("Resource")
        private List<String> Resource;
    }

    @Data
    @Builder
    public static class Principal {
        /**
         * AWS 主体列表,["*"] 表示所有用户
         */
        @JsonProperty("AWS")
        private List<String> AWS;
    }
}

上述代码中添加Bucket的访问策略的逻辑,主要是通过实体类BucketPolicyConfigDto生成了策略对应的JSON配置,生成配置如下:

 {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "",
        "Effect": "Allow",
        "Principal": {
          "AWS": ["*"]
        },
        "Action": ["s3:GetObject"],
        "Resource": ["arn:aws:s3:::image-hosting/*"]
      }
    ]
  }

Controller类

创建controller文件夹,并创建RustFSController.java实体类,实现文件上传、下载、删除功能。

RustFSController.java

package com.amoon.springrustfs.controller;

import cn.hutool.json.JSONUtil;
import com.amoon.springrustfs.common.api.RustFSUploadResult;
import com.amoon.springrustfs.config.BucketPolicyConfig;
import com.amoon.springrustfs.common.api.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;

import java.io.IOException;

/**
 * @ClassName: RustFSController
 * @Description: TODO
 * @Author: ZM
 * @Date: 2026年09月12日  22:33
 **/
@Slf4j
@Controller
@Tag(name = "RustFSController", description = "RustFS对象存储管理")
@RequestMapping("/rustfs")
public class RustFSController {

    @Autowired
    private S3Client s3Client;
    @Value("${rustfs.bucketName}")
    private String BUCKET_NAME;
    @Value("${rustfs.endpoint}")
    private String ENDPOINT;

    /**
     * 文件上传
     * @param file
     * @return
     */
    @Operation(summary = "文件上传")
    @RequestMapping(value = "/upload",method = RequestMethod.POST,consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public CommonResult<RustFSUploadResult> uploadFile(@RequestPart("file") MultipartFile file) {
        // TODO: 文件上传逻辑
        // 尝试创建桶,如果已存在则忽略
        try {
            s3Client.createBucket(CreateBucketRequest.builder()
                    .bucket(BUCKET_NAME)
                    .build());
            log.info("Bucket created: {}", BUCKET_NAME);
        } catch (BucketAlreadyExistsException | BucketAlreadyOwnedByYouException e) {
            log.info("Bucket already exists: {}", BUCKET_NAME);
        }
        // 设置Bucket的访问策略(幂等操作,重复设置也没关系)
        try {
            String policy = JSONUtil.toJsonStr(BucketPolicyConfig.createBucketPolicyConfigDto(BUCKET_NAME));
            log.info("Bucket policy: {}", policy);
            s3Client.putBucketPolicy(PutBucketPolicyRequest.builder()
                    .bucket(BUCKET_NAME)
                    .policy(policy)
                    .build());
        } catch (Exception e) {
            log.warn("Failed to set bucket policy: {}", e.getMessage());
        }

        // 上传文件
        try {
            s3Client.putObject(PutObjectRequest.builder()
                            .bucket(BUCKET_NAME)
                            .key(file.getOriginalFilename())
                            .contentType(file.getContentType())
                            .build(), RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
            RustFSUploadResult uploadResult = new RustFSUploadResult();
            uploadResult.setName(file.getOriginalFilename());
            uploadResult.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + file.getOriginalFilename());
            return CommonResult.success(uploadResult);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return CommonResult.error("上传失败");
    }

    /**
     * 文件下载
     * @param fileName
     * @return
     */
    @Operation(summary = "文件下载")
    @RequestMapping(value = "/download",method = RequestMethod.GET)
    @ResponseBody
    public void downloadFile(@RequestParam("fileName") String fileName, HttpServletResponse response) {
        // TODO: 文件下载逻辑
        try {
            // 1. 从 RustFS 获取文件流,返回给客户端
            ResponseInputStream<GetObjectResponse> s3Object = s3Client.getObject(
                    GetObjectRequest.builder()
                            .bucket(BUCKET_NAME)
                            .key(fileName)
                            .build());

            // 2. 设置响应头,告诉浏览器这是一个附件
            response.setContentType(s3Object.response().contentType());
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setContentLengthLong(s3Object.response().contentLength());

            // 3. 把文件流拷贝到 HttpServletResponse 的输出流
            StreamUtils.copy(s3Object, response.getOutputStream());
            response.flushBuffer();
        } catch (S3Exception e) {
            log.error("文件下载失败: {}", e.getMessage());
            throw new RuntimeException("文件下载失败");
        } catch (IOException e) {
            log.error("文件流写入失败: {}", e.getMessage());
            throw new RuntimeException("文件流写入失败");
        }
    }

    /**
     * 文件删除
     * @param fileName
     * @return
     */
    @Operation(summary = "文件删除")
    @RequestMapping(value = "/delete",method = RequestMethod.POST)
    @ResponseBody
    public CommonResult deleteFile(@RequestParam("fileName") String fileName) {
        //删除文件
        s3Client.deleteObject(DeleteObjectRequest.builder()
                .bucket(BUCKET_NAME)
                .key(fileName)
                .build());
        return CommonResult.success(null);
    }

}

功能测试

由于项目中集成了Swagger,.可以直接通过接口文档进行测试,选择好文件后调用文件上传接口, 访问地址:http://localhost:8080/swagger-ui.html

image-20260914224757734

image-20260914224937065

© 2024 - 2026 拾光舍

湘ICP备2025117227号-1 | 公安备案图标 湘公网安备43040002000293号
在线工具资源网站

在线工具官网

ProcessOn:👉ProcessOn在线作图

腾讯文档:👉腾讯文档

FlowUs 息流:👉FlowUs 息流

Mermaid Live Editor:👉在线流程图和图表编辑器 - 美人鱼实时编辑器

ME2在线工具(加密工具):👉ME2在线工具-在线开发学习生活工具箱

Textln:👉Textln文本智能云平台-通用文本解析

MinerU:👉MinerU数据提取工具

Crontab:👉Cron Expression Examples - Crontab.guru

正则表达式测试网站:👉regex101: build, test, and debug regex

技术资源网站

技术资源官网

Spring:👉Spring官网

MyBatis中文网 :👉MyBatis 中文网

Redis中文网:👉Redis中文网

Zookeeper:👉Apache ZooKeeper官网

Raft算法:👉Raft Consensus Algorithm官网

Hadoop:👉Apache Hadoop官网

Hive:👉Apache Hive官网

Phoenix:👉Apache Phoenix官网

Bootstrap中文网:👉Bootstrap中文网

Element:👉组件 | Element

Layui :👉Layui - 极简模块化前端 UI 组件库(官方文档)

FreeMarker :👉FreeMarker 中文官方参考手册

ztree树插件: 👉zTree – jQuery 树插件官网

x File Storage:👉x File Storage官网

社交链接

© 2024 - 2026 拾光舍

湘ICP备2025117227号-1 | 公安备案图标 湘公网安备43040002000293号