基于 RustFS 与 Spring Boot 构建高性能 S3 文件服务
2026-09-14T22:52:26+08:00 | 13分钟阅读

基于 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门覆盖;🧪 预览——以选择加入标志或有限制兼容性声明发布。
| Feature | Status | Feature | Status |
|---|---|---|---|
| S3 Core Features | ✅ Available | Distributed Mode | ✅ Available |
| Upload / Download | ✅ Available | Single Node Mode | ✅ Available |
| Versioning | ✅ Available | Bitrot Protection | ✅ Available |
| Object Lock (WORM) | ✅ Available | Healing & Scanner | ✅ Available |
| Server-Side Encryption | ✅ Available | Pool Expansion / Decommission | ✅ Available |
| RustFS KMS | ✅ Available | Bucket Replication | ✅ Available |
| Lifecycle Management (ILM) | ✅ Available | Site Replication | ✅ Available |
| ILM Tiering (Remote S3) | ✅ Available | Bucket Quota | ✅ Available |
| S3 Select | ✅ Available | Event Notifications | ✅ Available |
| S3 Tables (Iceberg REST) | 🧪 Preview | Audit Logging | ✅ Available |
| IAM / Policies | ✅ Available | Logging & Observability | ✅ Available |
| OIDC / SSO | ✅ Available | Web Console | ✅ Available |
| Keystone Auth | ✅ Available | K8s Helm Charts | ✅ Available |
| Swift API | ✅ Available | FTPS / WebDAV | ✅ Available |
| Multi-Tenancy | ✅ Available | SFTP | ✅ 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 性能
压力测试环境:
| Type | Parameter | Remark |
|---|---|---|
| CPU | 2 Core | Intel Xeon (Sapphire Rapids) Platinum 8475B, 2.7/3.2 GHz |
| Memory | 4GB | |
| Network | 15Gbps | |
| Drive | 40GB x 4 | IOPS 3800 / Drive |
RustFS vs Other Object Storage
| Feature | RustFS | Other Object Storage |
|---|---|---|
| Console Experience | Powerful Console Comprehensive management interface. | Basic / Limited Console Often overly simple or lacking critical features. |
| Language & Safety | Rust-based Memory safety by design. | Go or C-based Potential for memory GC pauses or leaks. |
| Data Sovereignty | No 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. |
| Licensing | Permissive Apache 2.0 Business-friendly, no “poison pill” clauses. | Restrictive AGPL v3 Risk of license traps and intellectual property pollution. |
| Compatibility | S3-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 & IoT | Strong Edge Support Ideal for secure, innovative edge devices. | Weak Edge Support Often too heavy for edge gateways. |
| Risk Profile | Enterprise 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
设置凭证
在
/etc/default/rustfs中设置:[root@hcss-ecs-9434 ~]# vim /etc/default/rustfsRUSTFS_ACCESS_KEY=<your-access-key> RUSTFS_SECRET_KEY=<your-secret-key> ; e.g. output of: openssl rand -base64 24
注意:
- 访问密钥请仅使用大写字母和数字。请勿直接将未经转换的 Base64 输出用于
RUSTFS_ACCESS_KEY,因为 Base64 可能包含/,与 AWS Signature Version 4 的凭证作用域分隔符冲突。 - 不要保留默认凭证。如果未设置
RUSTFS_ACCESS_KEY/RUSTFS_SECRET_KEY,服务器会回退到内置默认值rustfsadmin/rustfsadmin。该值只能用于一次性本地测试,绝不能用于他人可访问的环境。
- 访问密钥请仅使用大写字母和数字。请勿直接将未经转换的 Base64 输出用于
按
Esc,输入:wq然后回车。重启服务使配置生效 修改完配置后,需要重启 RustFS 服务:
[root@hcss-ecs-9434 ~]# sudo systemctl restart rustfs检查服务状态
确认服务是否正常运行:
[root@hcss-ecs-9434 ~]# sudo systemctl status rustfs --no-pager # should report: active (running)如果看到绿色的
active (running)字样,就说明一切正常了。
登录控制台
在浏览器中打开 http://<server-ip>:9001,使用访问密钥RUSTFS_ACCESS_KEY和秘密密钥登录RUSTFS_SECRET_KEY。

提示:如果你的服务器有防火墙或云服务商的安全组,请确保放行了
9000和9001这两个 TCP 端口,否则浏览器可能无法打开页面。
创建存储桶并上传文件
在控制台首页选择 创建存储桶,输入名称(例如
my-bucket)并确认。

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


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

扩展
浏览器打不开页面(无法访问)
通常是防火墙或云服务商安全组拦截了端口。请检查以下两点:
- 云服务器安全组:登录你的云服务商控制台(如阿里云、腾讯云、华为云等),在安全组/防火墙规则中,检查是否放行了 TCP 9000 和 9001 端口。
- 系统防火墙:在服务器上是否开启了
firewalld或ufw。
云服务器安全组放行TCP 9000 和 9001端口
云服务器安全组:登录你的云服务商控制台(如阿里云、腾讯云、华为云等),在安全组/防火墙规则中,放行 TCP 9000 和 9001 端口。

具体步骤:
选择“入方向规则”。
点击左侧的“添加规则”按钮(在“一键放通常用端口”旁边)。
在弹出的窗口中,按照以下参数填写(以华为云为例,大同小异):
- 优先级:填写**
1**即可。 - 类型:选择
自定义 TCP(或者直接选 TCP)。 - 协议端口:
- 如果只想放行 RustFS 所需的端口,填入:
9000,9001(有的控制台可能需要分开填,比如填两行,一行 9000,一行 9001)。 - 如果觉得麻烦,或者以后还要部署其他服务,也可以直接选“
一键放通常用端口”,但那会开放很多端口,不够安全。建议自定义放行9000,9001。
- 如果只想放行 RustFS 所需的端口,填入:
- 源地址:
- 推荐:默认填
0.0.0.0/0。这代表允许互联网上任何 IP 访问你的这两个端口(方便回家、在公司等不同网络访问)。 - 更安全(可选):如果你有固定的办公或家庭公网 IP,可以只填自己的 IP 地址(例如
123.123.123.123/32),这样只有自己能连,最安全。
- 推荐:默认填
- 描述:可以填“RustFS 服务与控制台”,方便以后辨认。
- 策略:必须是 允许。
- 优先级:填写**
点击“保存”或“确定”。添加完规则后,不需要重启服务器,安全组规则通常会立即生效。
系统防火墙放行TCP 9000 和 9001 端口
系统防火墙:如果在服务器上开启了 firewalld 或 ufw,也需要放行这两个端口。例如在 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控制台
具体步骤:
添加站点:进入宝塔面板的 网站 → 添加站点,填写你的域名(如
console.你的域名.com),PHP 版本选择“纯静态”即可,提交后会生成一个用于反向代理的空站点。

配置反向代理:在网站列表找到该站点,点击 设置 → 反向代理 → 添加反向代理。
代理名称:自定义(如
RustFS-Console和)。目标 URL:填写
http://127.0.0.1:9001(即 RustFS 控制台的本机地址)。发送域名:保持默认的
$http_host即可,一般无需修改。开启缓存:关闭**“开启缓存”** (变成灰色)。
原因:RustFS 是用来做对象存储的,文件传输本身不需要 Nginx 再做一层缓存,开启反而容易导致大文件传输出错 或占用大量磁盘。


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

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

配置反向代理:在网站列表找到该站点,点击 设置 → 反向代理 → 添加反向代理。
代理名称:自定义(如
RustFS-Console和)。目标 URL:填写
http://127.0.0.1:9001(即 RustFS 控制台的本机地址)。发送域名:保持默认的
$http_host即可,一般无需修改。开启缓存:关闭**“开启缓存”** (变成灰色)。
原因:RustFS 是用来做对象存储的,文件传输本身不需要 Nginx 再做一层缓存,开启反而容易导致大文件传输出错 或占用大量磁盘。


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

补充与建议
重要补充(RustFS 特殊配置)
由于 RustFS 是对象存储服务,且之前安装了 systemd 服务,建议给 Nginx 配置添加特殊指令,以避免访问存储桶时出现 Bucket not found 错误。
根据社区反馈,需要在 Nginx 配置文件中添加 proxy_cache_convert_head off; 指令,否则存储桶访问会报错。
具体步骤:
在Rustfs控制台网站的反向代理中,点击配置文件,编辑配置文件,添加proxy_cache_convert_head off


安全建议
由于 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

添加依赖项
在 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 refused | RustFS 未启动或端口不正确 | 检查 RustFS 状态和端口 |
403 Forbidden | AccessKey / SecretKey 错误 | 检查身份验证配置 |
| 上传失败且无响应 | SDK 默认使用 HTTPS,而 RustFS 仅支持 HTTP(或需要证书) | 使用 http:// 地址并配置 endpointOverride |
Spring Boot项目
结合Spring Boot实现文件上传、下载、删除。
创建项目


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连接配置信息。
在resource的application.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.java和BucketPolicyConfig.java两个配置类。

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.java和RustFSUploadResult.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



湘公网安备43040002000293号