前言
在现代互联网应用中,文件上传是一个常见的功能需求。对于大文件上传,传统的单次上传方式可能会导致网络不稳定、上传失败等问题。这次将介绍如何使用SpringBoot和Minio实现文件切片上传,解决大文件上传的痛点,同时支持秒传和续传功能。
一、Minio简介
Minio是一款开源的对象存储服务器,支持多种操作系统(如Linux、Windows、MacOS等),并提供了简单、可扩展、高可用的对象存储解决方案。Minio的主要特点包括:
-
简单易用:安装和配置简单,提供Web UI管理界面。
-
可扩展性:支持单节点、多节点部署,可扩展到多个节点。
-
高可用性:支持数据冗余备份、数据复制和故障转移。
-
安全性:支持SSL/TLS加密、访问控制和数据加密。
-
多语言支持:支持Java、Python、Go等多种编程语言。
-
对象存储:支持任意数量和大小的对象存储。
Minio适用于多种场景,包括大规模数据存储、图像和媒体存储、云原生应用、数据保护与灾难恢复、分布式计算和机器学习等。
二、Minio安装与配置
1. 安装Minio
使用Docker安装Minio
# 拉取Minio镜像
docker pull minio/minio
# 启动Minio容器
docker run -p 9000:9000 -p 9090:9090 \
--name minio \
-d --restart=always \
-e "MINIO_ACCESS_KEY=IT@WangHui" \
-e "MINIO_SECRET_KEY=IT@WangHui" \
minio/minio server /data --console-address ":9000" --address ":9090"
-
9000
:Web管理界面端口 -
9090
:服务端口 -
MINIO_ACCESS_KEY
和MINIO_SECRET_KEY
:访问凭证
防火墙配置
确保防火墙允许访问9000和9090端口:
firewall-cmd --zone=public --add-port=9000/tcp --permanent
firewall-cmd --zone=public --add-port=9090/tcp --permanent
firewall-cmd --reload
2. 访问Minio
启动后,通过浏览器访问 http://localhost:9000
,使用启动时设置的用户名和密码登录。
三、SpringBoot集成Minio
1. 项目依赖
在SpringBoot项目中引入Minio的Java SDK依赖:
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.2</version>
</dependency>
2. 配置文件
在 application.yml
中配置Minio相关信息:
spring:
minio:
access-key: dAMaxkWaXUD1CV1JHbqw
secret-key: AXt3SD0JFkDENFbMeJKOOQb5wj8KvabZWu33Rs84
url: http://192.168.18.14:9090
bucket-name: wanghui
3. Minio配置类
创建一个配置类,用于初始化Minio客户端:
@Configuration
@ConfigurationProperties(prefix = "spring.minio")
public class MinioConfig {
private String accessKey;
private String secretKey;
private String url;
private String bucketName;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(url)
.credentials(accessKey, secretKey)
.build();
}
}
4. Minio工具类
创建工具类,封装文件上传、下载等操作:
@Component
public class MinioUtils {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioConfig configuration;
public boolean existBucket(String name) {
try {
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
if (!exists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public void upload(MultipartFile file, String fileName) {
try {
InputStream inputStream = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(configuration.getBucketName())
.object(fileName)
.stream(inputStream, file.getSize(), -1)
.contentType(file.getContentType())
.build());
} catch (Exception e) {
e.printStackTrace();
}
}
public ResponseEntity<byte[]> download(String fileName) {
try {
InputStream in = minioClient.getObject(GetObjectArgs.builder()
.bucket(configuration.getBucketName())
.object(fileName)
.build());
// 处理下载逻辑
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
四、实现文件切片上传
1. 前端切片上传
前端使用JavaScript将文件切片,并逐个上传到Minio:
function uploadFile(file) {
const chunkSize = 1024 * 1024 * 5; // 每片5MB
const chunks = Math.ceil(file.size / chunkSize);
const fileSlice = File.prototype.slice || File.prototype.webkitSlice || File.prototype.mozSlice;
for (let i = 0; i < chunks; i++) {
const start = i * chunkSize;
const end = Math.min(file.size, start + chunkSize);
const chunk = fileSlice.call(file, start, end);
uploadChunk(chunk, i, chunks);
}
}
function uploadChunk(chunk, chunkIndex, totalChunks) {
// 调用后端接口上传切片
const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', totalChunks);
fetch('/api/uploadChunk', {
method: 'POST',
body: formData
}).then(response => response.json())
.then(data => console.log(data));
}
2. 后端合并切片
后端接收到所有切片后,按照顺序合并成完整文件:
@PostMapping("/uploadChunk")
public AjaxResult uploadChunk(@RequestParam("file") MultipartFile file,
@RequestParam("chunkIndex") Integer chunkIndex,
@RequestParam("totalChunks") Integer totalChunks,
@RequestParam("fileName") String fileName) {
// 存储切片
String chunkName = fileName + "_chunk_" + chunkIndex;
minioUtils.upload(file, chunkName);
// 检查是否所有切片都已上传
if (chunkIndex == totalChunks - 1) {
mergeChunks(fileName, totalChunks);
}
return AjaxResult.success("切片上传成功");
}
private void mergeChunks(String fileName, Integer totalChunks) {
List<String> chunkNames = new ArrayList<>();
for (int i = 0; i < totalChunks; i++) {
chunkNames.add(fileName + "_chunk_" + i);
}
// 合并切片
minioUtils.mergeObjects(chunkNames, fileName);
}
五、功能测试
1. 大文件上传测试
上传一个大文件(如1GB视频),观察上传速度和稳定性。
2. 断点续传测试
模拟网络中断,重新上传未完成的切片,验证续传功能。
3. 秒传功能测试
上传一个已存在的文件,验证是否直接返回文件URL。
六、总结
通过SpringBoot和Minio实现文件切片上传,可以有效解决大文件上传的性能问题,同时支持断点续传和秒传功能。Minio的高可用性和扩展性使其成为理想的对象存储解决方案。