在日常开发中,对象存储的集成总让人头疼?本文通过完整可运行的java代码,手把手教你实现rustfs的实时上传下载功能,带你彻底告别集成难题!
一、环境准备:5分钟快速搭建rustfs服务
1.1 docker一键启动rustfs
首先使用docker快速搭建rustfs服务环境:
# 创建项目目录
mkdir rustfs-java-demo && cd rustfs-java-demo
# 创建docker-compose.yml
cat > docker-compose.yml << 'eof'
version: '3.8'
services:
rustfs:
image: rustfs/rustfs:latest
container_name: rustfs-server
ports:
- "9000:9000" # s3 api端口
- "9001:9001" # 控制台端口
environment:
rustfs_access_key: "admin"
rustfs_secret_key: "password123"
volumes:
- ./data:/data
restart: unless-stopped
eof
# 启动服务
docker-compose up -d
# 验证服务状态
docker ps | grep rustfs1.2 验证服务可用性
# 检查服务健康状态 curl http://localhost:9000/minio/health/live # 访问web控制台 echo "控制台地址: http://localhost:9001" echo "用户名: admin" echo "密码: password123"
二、java项目配置:依赖引入与客户端初始化
2.1 maven依赖配置
创建spring boot项目,在pom.xml中添加必要依赖:
<dependencies>
<!-- spring boot starter -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>
<!-- aws s3 sdk -->
<dependency>
<groupid>software.amazon.awssdk</groupid>
<artifactid>s3</artifactid>
<version>2.20.0</version>
</dependency>
<!-- 文件操作工具 -->
<dependency>
<groupid>commons-io</groupid>
<artifactid>commons-io</artifactid>
<version>2.13.0</version>
</dependency>
</dependencies>2.2 rustfs配置类
创建配置类rustfsconfig.java:
package com.rustfs.demo.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;
@configuration
public class rustfsconfig {
@value("${rustfs.endpoint:http://localhost:9000}")
private string endpoint;
@value("${rustfs.access-key:admin}")
private string accesskey;
@value("${rustfs.secret-key:password123}")
private string secretkey;
@value("${rustfs.region:us-east-1}")
private string region;
@bean
public s3client s3client() {
return s3client.builder()
.endpointoverride(uri.create(endpoint))
.credentialsprovider(staticcredentialsprovider.create(
awsbasiccredentials.create(accesskey, secretkey)))
.region(region.of(region))
.forcepathstyle(true) // 必须设置为true用于兼容rustfs
.build();
}
}2.3 应用配置文件
application.yml配置:
rustfs:
endpoint: "http://localhost:9000"
access-key: "admin"
secret-key: "password123"
region: "us-east-1"
bucket-name: "my-bucket"
server:
port: 8080
spring:
servlet:
multipart:
max-file-size: 10mb
max-request-size: 10mb三、核心服务层:上传下载功能实现
3.1 存储服务接口设计
创建storageservice.java接口:
package com.rustfs.demo.service;
import org.springframework.web.multipart.multipartfile;
import java.io.inputstream;
public interface storageservice {
/**
* 上传文件到rustfs
*/
string uploadfile(multipartfile file, string objectname);
/**
* 下载文件从rustfs
*/
inputstream downloadfile(string objectname);
/**
* 获取文件访问url
*/
string getfileurl(string objectname);
/**
* 删除文件
*/
boolean deletefile(string objectname);
}3.2 rustfs服务实现类
实现类rustfsservice.java:
package com.rustfs.demo.service.impl;
import lombok.extern.slf4j.slf4j;
import org.springframework.beans.factory.annotation.value;
import org.springframework.stereotype.service;
import org.springframework.web.multipart.multipartfile;
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;
import java.io.inputstream;
import java.time.duration;
@service
@slf4j
public class rustfsservice implements storageservice {
private final s3client s3client;
@value("${rustfs.bucket-name:my-bucket}")
private string bucketname;
public rustfsservice(s3client s3client) {
this.s3client = s3client;
createbucketifnotexists();
}
/**
* 检查并创建存储桶
*/
private void createbucketifnotexists() {
try {
s3client.headbucket(headbucketrequest.builder()
.bucket(bucketname)
.build());
log.info("存储桶 {} 已存在", bucketname);
} catch (nosuchbucketexception e) {
// 存储桶不存在,创建新桶
s3client.createbucket(createbucketrequest.builder()
.bucket(bucketname)
.build());
log.info("创建存储桶: {}", bucketname);
}
}
@override
public string uploadfile(multipartfile file, string objectname) {
try {
// 如果未指定对象名称,使用原始文件名
if (objectname == null || objectname.trim().isempty()) {
objectname = file.getoriginalfilename();
}
// 执行文件上传
putobjectrequest putobjectrequest = putobjectrequest.builder()
.bucket(bucketname)
.key(objectname)
.contenttype(file.getcontenttype())
.build();
s3client.putobject(putobjectrequest,
requestbody.frombytes(file.getbytes()));
log.info("文件上传成功: {}", objectname);
return objectname;
} catch (ioexception e) {
log.error("文件上传失败: {}", e.getmessage());
throw new runtimeexception("文件上传失败", e);
}
}
@override
public inputstream downloadfile(string objectname) {
try {
getobjectrequest getobjectrequest = getobjectrequest.builder()
.bucket(bucketname)
.key(objectname)
.build();
return s3client.getobject(getobjectrequest);
} catch (nosuchkeyexception e) {
log.error("文件不存在: {}", objectname);
throw new runtimeexception("文件不存在: " + objectname);
}
}
@override
public string getfileurl(string objectname) {
try {
geturlrequest geturlrequest = geturlrequest.builder()
.bucket(bucketname)
.key(objectname)
.build();
return s3client.utilities().geturl(geturlrequest).tostring();
} catch (exception e) {
log.error("获取文件url失败: {}", e.getmessage());
return null;
}
}
@override
public boolean deletefile(string objectname) {
try {
deleteobjectrequest deleteobjectrequest = deleteobjectrequest.builder()
.bucket(bucketname)
.key(objectname)
.build();
s3client.deleteobject(deleteobjectrequest);
log.info("文件删除成功: {}", objectname);
return true;
} catch (exception e) {
log.error("文件删除失败: {}", e.getmessage());
return false;
}
}
}四、restful api控制器:提供http接口
4.1 文件上传下载控制器
创建filecontroller.java:
package com.rustfs.demo.controller;
import com.rustfs.demo.service.storageservice;
import lombok.extern.slf4j.slf4j;
import org.apache.commons.io.ioutils;
import org.springframework.http.httpheaders;
import org.springframework.http.httpstatus;
import org.springframework.http.mediatype;
import org.springframework.http.responseentity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.multipartfile;
import java.io.inputstream;
import java.nio.charset.standardcharsets;
import java.util.hashmap;
import java.util.map;
@restcontroller
@requestmapping("/api/files")
@slf4j
public class filecontroller {
private final storageservice storageservice;
public filecontroller(storageservice storageservice) {
this.storageservice = storageservice;
}
/**
* 文件上传接口
*/
@postmapping("/upload")
public responseentity<map<string, object>> uploadfile(
@requestparam("file") multipartfile file,
@requestparam(value = "objectname", required = false) string objectname) {
try {
string uploadedobjectname = storageservice.uploadfile(file, objectname);
string fileurl = storageservice.getfileurl(uploadedobjectname);
map<string, object> result = new hashmap<>();
result.put("success", true);
result.put("objectname", uploadedobjectname);
result.put("fileurl", fileurl);
result.put("filesize", file.getsize());
result.put("message", "文件上传成功");
return responseentity.ok(result);
} catch (exception e) {
log.error("文件上传失败: {}", e.getmessage());
return responseentity.status(httpstatus.internal_server_error)
.body(map.of("success", false, "message", "文件上传失败: " + e.getmessage()));
}
}
/**
* 文件下载接口
*/
@getmapping("/download/{objectname}")
public responseentity<byte[]> downloadfile(@pathvariable string objectname) {
try {
inputstream inputstream = storageservice.downloadfile(objectname);
byte[] filebytes = ioutils.tobytearray(inputstream);
httpheaders headers = new httpheaders();
headers.setcontenttype(mediatype.application_octet_stream);
headers.setcontentdispositionformdata("attachment",
new string(objectname.getbytes(standardcharsets.utf_8), standardcharsets.iso_8859_1));
return new responseentity<>(filebytes, headers, httpstatus.ok);
} catch (exception e) {
log.error("文件下载失败: {}", e.getmessage());
return responseentity.status(httpstatus.not_found).build();
}
}
/**
* 获取文件信息
*/
@getmapping("/info/{objectname}")
public responseentity<map<string, object>> getfileinfo(@pathvariable string objectname) {
try {
string fileurl = storageservice.getfileurl(objectname);
map<string, object> result = new hashmap<>();
result.put("objectname", objectname);
result.put("fileurl", fileurl);
result.put("exists", true);
return responseentity.ok(result);
} catch (exception e) {
return responseentity.ok(map.of(
"objectname", objectname,
"exists", false,
"message", "文件不存在"
));
}
}
/**
* 删除文件接口
*/
@deletemapping("/{objectname}")
public responseentity<map<string, object>> deletefile(@pathvariable string objectname) {
try {
boolean success = storageservice.deletefile(objectname);
map<string, object> result = new hashmap<>();
result.put("success", success);
result.put("message", success ? "文件删除成功" : "文件删除失败");
return responseentity.ok(result);
} catch (exception e) {
log.error("文件删除失败: {}", e.getmessage());
return responseentity.status(httpstatus.internal_server_error)
.body(map.of("success", false, "message", "文件删除失败"));
}
}
}五、高级功能:大文件分片上传
5.1 分片上传服务类
对于大文件,实现分片上传功能:
package com.rustfs.demo.service.impl;
import org.springframework.stereotype.service;
import org.springframework.web.multipart.multipartfile;
import software.amazon.awssdk.core.sync.requestbody;
import software.amazon.awssdk.services.s3.s3client;
import software.amazon.awssdk.services.s3.model.*;
import java.util.arraylist;
import java.util.comparator;
import java.util.list;
@service
public class multipartuploadservice {
private final s3client s3client;
private static final int part_size = 5 * 1024 * 1024; // 5mb
public multipartuploadservice(s3client s3client) {
this.s3client = s3client;
}
/**
* 大文件分片上传
*/
public string uploadlargefile(multipartfile file, string bucketname, string objectname)
throws exception {
// 初始化分片上传
createmultipartuploadrequest createrequest = createmultipartuploadrequest.builder()
.bucket(bucketname)
.key(objectname)
.contenttype(file.getcontenttype())
.build();
createmultipartuploadresponse createresponse = s3client.createmultipartupload(createrequest);
string uploadid = createresponse.uploadid();
list<completedpart> completedparts = new arraylist<>();
byte[] filebytes = file.getbytes();
int partcount = (int) math.ceil((double) filebytes.length / part_size);
// 上传各个分片
for (int partnumber = 1; partnumber <= partcount; partnumber++) {
int start = (partnumber - 1) * part_size;
int end = math.min(start + part_size, filebytes.length);
byte[] partbytes = new byte[end - start];
system.arraycopy(filebytes, start, partbytes, 0, partbytes.length);
uploadpartrequest uploadrequest = uploadpartrequest.builder()
.bucket(bucketname)
.key(objectname)
.uploadid(uploadid)
.partnumber(partnumber)
.build();
uploadpartresponse uploadresponse = s3client.uploadpart(uploadrequest,
requestbody.frombytes(partbytes));
completedparts.add(completedpart.builder()
.partnumber(partnumber)
.etag(uploadresponse.etag())
.build());
}
// 完成分片上传
completedmultipartupload completedmultipartupload = completedmultipartupload.builder()
.parts(completedparts)
.build();
completemultipartuploadrequest completerequest = completemultipartuploadrequest.builder()
.bucket(bucketname)
.key(objectname)
.uploadid(uploadid)
.multipartupload(completedmultipartupload)
.build();
s3client.completemultipartupload(completerequest);
return objectname;
}
}六、功能测试与验证
6.1 使用curl测试api接口
# 测试文件上传 curl -x post -f "file=@test.jpg" \ http://localhost:8080/api/files/upload # 测试文件下载 curl -x get \ http://localhost:8080/api/files/download/test.jpg \ --output downloaded.jpg # 测试文件信息查询 curl -x get \ http://localhost:8080/api/files/info/test.jpg # 测试文件删除 curl -x delete \ http://localhost:8080/api/files/test.jpg
6.2 使用postman测试
创建测试集合,包含以下请求:
- post
/api/files/upload- 文件上传 - get
/api/files/download/{name}- 文件下载 - get
/api/files/info/{name}- 文件信息 - delete
/api/files/{name}- 文件删除
七、生产环境优化建议
7.1 连接池配置优化
@bean
public s3client s3client() {
return s3client.builder()
.endpointoverride(uri.create(endpoint))
.credentialsprovider(staticcredentialsprovider.create(
awsbasiccredentials.create(accesskey, secretkey)))
.region(region.of(region))
.forcepathstyle(true)
.httpclientbuilder(urlconnectionhttpclient.builder()
.maxconnections(100) // 最大连接数
.connectiontimeout(duration.ofseconds(10)) // 连接超时
.sockettimeout(duration.ofseconds(30))) // 读写超时
.build();
}7.2 异常处理增强
创建全局异常处理器:
@restcontrolleradvice
public class globalexceptionhandler {
@exceptionhandler(s3exception.class)
public responseentity<map<string, object>> handles3exception(s3exception e) {
log.error("rustfs操作异常: {}", e.getmessage());
map<string, object> error = new hashmap<>();
error.put("success", false);
error.put("errorcode", e.awserrordetails().errorcode());
error.put("message", "存储服务异常: " + e.getmessage());
return responseentity.status(httpstatus.internal_server_error).body(error);
}
}八、完整项目结构
src/main/java/
└── com/rustfs/demo/
├── config/
│ └── rustfsconfig.java
├── controller/
│ └── filecontroller.java
├── service/
│ ├── storageservice.java
│ ├── impl/
│ │ ├── rustfsservice.java
│ │ └── multipartuploadservice.java
│ └── exception/
│ └── globalexceptionhandler.java
└── demoapplication.java总结
通过本文的完整实现,我们成功构建了一个基于java和docker的rustfs文件存储服务。关键亮点包括:
✅ 开箱即用:提供完整可运行的代码示例
✅ 功能全面:覆盖上传、下载、删除等核心操作
✅ 生产就绪:包含异常处理、性能优化等生产级特性
✅ 易于扩展:模块化设计便于功能扩展
这种实现方式特别适合需要快速集成对象存储功能的java项目,无论是单体应用还是微服务架构都能良好适配。
以下是深入学习 rustfs 的推荐资源:rustfs
官方文档: rustfs 官方文档- 提供架构、安装指南和 api 参考。
github 仓库: github 仓库 - 获取源代码、提交问题或贡献代码。
社区支持: github discussions- 与开发者交流经验和解决方案。
到此这篇关于rustfs接口实战之java+docker实现实时上传下载功能的文章就介绍到这了,更多相关java docker实时上传下载内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论