MinIO分布式存储与Vue+SpringBoot全栈整合开发指南(含代码实现)
一、MinIO分布式存储的部署与配置
1. MinIO单机版安装与配置
安装步骤:
- 下载MinIO二进制文件(Linux系统为例):
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
- 创建数据存储目录并启动服务:
mkdir -p ~/minio/data
./minio server ~/minio/data --console-address ":9001"
- 访问Web管理界面(默认端口9001):
http://your-server-ip:9001
2. MinIO分布式集群部署
4节点集群示例配置:
export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=your-strong-password
minio server http://node{1...4}/mnt/data{1...4} \
--console-address ":9001"
关键配置说明:
- 每个节点至少需要4块磁盘
- 数据目录建议使用XFS文件系统
- 生产环境建议配置TLS证书
二、SpringBoot后端集成MinIO
1. 添加依赖配置
pom.xml依赖:
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.2</version>
</dependency>
application.yml配置:
minio:
endpoint: http://your-minio-server:9000
accessKey: your-access-key
secretKey: your-secret-key
bucket: default-bucket
2. 核心服务实现
MinIO配置类:
@Configuration
public class MinioConfig {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}
文件上传服务:
@Service
public class FileStorageService {
@Autowired
private MinioClient minioClient;
@Value("${minio.bucket}")
private String bucketName;
public String uploadFile(MultipartFile file) throws Exception {
String objectName = UUID.randomUUID() + "-" + file.getOriginalFilename();
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build());
return objectName;
}
}
三、Vue前端实现文件管理
1. 文件上传组件实现
FileUpload.vue:
<template>
<div>
<input type="file" @change="handleFileChange" />
<button @click="uploadFile">上传</button>
<progress v-if="uploading" :value="progress" max="100"></progress>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
selectedFile: null,
uploading: false,
progress: 0
};
},
methods: {
handleFileChange(event) {
this.selectedFile = event.target.files[0];
},
async uploadFile() {
if (!this.selectedFile) return;
const formData = new FormData();
formData.append('file', this.selectedFile);
try {
this.uploading = true;
const response = await axios.post('/api/files/upload', formData, {
onUploadProgress: progressEvent => {
this.progress = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
}
});
this.$emit('upload-success', response.data);
} catch (error) {
console.error('上传失败:', error);
} finally {
this.uploading = false;
}
}
}
};
</script>
2. 文件列表展示组件
FileList.vue:
<template>
<div>
<table>
<thead>
<tr>
<th>文件名</th>
<th>大小</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="file in files" :key="file.name">
<td>{{ file.name }}</td>
<td>{{ formatFileSize(file.size) }}</td>
<td>
<button @click="downloadFile(file.name)">下载</button>
<button @click="previewFile(file.name)">预览</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
props: {
files: Array
},
methods: {
formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
},
downloadFile(filename) {
window.open(`/api/files/download/${filename}`, '_blank');
},
previewFile(filename) {
this.$emit('preview', filename);
}
}
};
</script>
四、高级功能实现
1. 大文件分片上传(后端)
public String uploadLargeFile(MultipartFile file, int chunkNumber,
int totalChunks, String fileId) throws Exception {
String tempDir = System.getProperty("java.io.tmpdir");
Path tempPath = Paths.get(tempDir, fileId);
// 写入分片
Files.write(tempPath.resolve(chunkNumber + ".part"),
file.getBytes(), StandardOpenOption.CREATE);
// 检查是否所有分片都已上传
if (Files.list(tempPath).count() == totalChunks) {
// 合并文件
Path mergedFile = mergeFiles(tempPath, fileId, file.getOriginalFilename());
// 上传到MinIO
String objectName = fileId + "-" + file.getOriginalFilename();
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(Files.newInputStream(mergedFile),
Files.size(mergedFile), -1)
.build());
// 清理临时文件
FileUtils.deleteDirectory(tempPath.toFile());
return objectName;
}
return "chunk_uploaded";
}
2. 文件预览服务
@GetMapping("/preview/{objectName}")
public ResponseEntity<Resource> previewFile(
@PathVariable String objectName) throws Exception {
InputStream stream = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
String contentType = minioClient.statObject(
StatObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build()).contentType();
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION,
"inline; filename=\"" + objectName + "\"")
.body(new InputStreamResource(stream));
}
五、安全与权限控制
1. 存储桶策略配置
public void setBucketPolicy(String bucketName) throws Exception {
String policyJson = """
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": ["*"]},
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::%s/*"],
"Condition": {
"IpAddress": {"aws:SourceIp": ["192.168.1.0/24"]}
}
}
]
}
""".formatted(bucketName);
minioClient.setBucketPolicy(
SetBucketPolicyArgs.builder()
.bucket(bucketName)
.config(policyJson)
.build());
}
2. 临时访问URL生成
public String generatePresignedUrl(String objectName,
int expiryDuration, TimeUnit timeUnit) throws Exception {
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(objectName)
.expiry(expiryDuration, timeUnit)
.build());
}
以上代码实现展示了MinIO与Vue+SpringBoot全栈整合的核心功能,包括基础文件上传下载、大文件分片处理、文件预览以及安全控制等关键功能模块。实际开发中可根据业务需求进行扩展和优化。