场景
若依前后端分离版手把手教你本地搭建环境并运行项目:
若依前后端分离版手把手教你本地搭建环境并运行项目-CSDN博客
参考上面搭建项目。
Minio
Minio是基于Go语言编写的对象存储服务,适合于存储大容量非结构化的数据,例如图片、音频、视频、日志文件、备份数据和容器/虚拟机镜像等,
而一个对象文件可以是任意大小,从几kb到最大5T不等。区别于分布式存储系统,minio的特色在于简单、轻量级,对开发者友好。
特点
简单、可靠:
Minio采用简单可靠的集群方案,摒弃复杂的大规模的集群调度管理,减少风险与性能瓶颈,聚焦产品的核心功能,
打造高可用的集群、灵活的扩展能力以及超过的性能。建立众多的中小规模、易管理的集群,
支持跨数据中心将多个集群聚合成超大资源池,而非直接采用大规模、统一管理的分布式集群。
功能完善:
Minio支持云原生,能与Kubernetes、Docker、Swarm编排系统良好对接,实现灵活部署。
且部署简单,只有一个可执行文件,参数极少,一条命令即可启动一个Minio系统。
Minio为了高性能采取无元数据数据库设计,避免元数据库成为整个系统的性能瓶颈,
并将故障限制在单个集群之内,从而不会涉及其他集群。Minio同时完全兼容S3接口,
因此也可以作为网关使用,对外提供S3访问。同时使用Minio Erasure code和checksum 来防止硬件故障。
即使损失一半以上的硬盘,但是仍然可以从中恢复。分布式中也允许(N/2)-1个节点故障。
官方文档:
MinIO | 高性能, Kubernetes原生对象存储
Java快速指南:
Java快速指南 — MinIO中文文档 | MinIO Linux中文文档
注:
博客:
霸道流氓气质-CSDN博客
实现
1、Minio在Windows上下载安装启动
MinIO下载和安装 | 用于创建高性能对象存储的代码和下载内容
下载之后只有一个minio.exe,然后新建一个文件存储路径,这里是D:\minioData
在minio.exe所在的目录下打开cmd,输入
minio.exe server D:\minioData
后面跟着指定存储的目录
提示是因为未修改默认密码。
启动之后访问
http://127.0.0.1:9000/
这里会自动跳转到11466端口。
输入登录用户名密码,默认都为
minioadmin
2、SpringBoot中整合Minio实现客户端
添加项目依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.2</version>
</dependency>
新增yml配置文件内容
minio:
server: http://127.0.0.1
port: 9000
accessKey: minioadmin
secretKey: minioadmin
配置minio的ip、端口、用户名、密码
然后新建配置类,读取配置文件内容并建立minio连接
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "minio")
@Data
public class MinioConfig{
private String server;
private int port;
private String accessKey;
private String secretKey;
/**
* 创建minio连接对象
* @return
*/
@Bean
public MinioClient minioClient(){
return MinioClient.builder()
.endpoint(server,port,false)
.credentials(accessKey,secretKey)
.build();
}
}
3、SpringBoot中操作Minio的工具类和使用示例
Minio中使用Bucket桶的概念,类似文件目录,一般一个项目中使用一个桶。
新建Minio工具类
import cn.hutool.core.io.FastByteArrayOutputStream;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Component
public class MinioUtils {
@Autowired
MinioClient minioClient;
public final String PREFIX = "minio/";
/**
* 查看存储bucket是否存在
* bucketName 需要传入桶名
* @return boolean
*/
public Boolean bucketExists(String bucketName) {
Boolean found;
try {
found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return found;
}
/**
* 创建存储bucket
* bucketName 需要传入桶名
* @return Boolean
*/
public Boolean makeBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 删除存储bucket
* bucketName 需要传入桶名
* @return Boolean
*/
public Boolean removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 获取全部bucket
*/
public List<Bucket> getAllBuckets() {
try {
List<Bucket> buckets = minioClient.listBuckets();
return buckets;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 文件上传
*
* @param file 文件
* @param bucketName bucketName
* BucketName 需要传入桶名
* @return Boolean
*/
public String upload(String bucketName,MultipartFile file) {
String originalFilename = file.getOriginalFilename();
if (StringUtils.isBlank(originalFilename)){
throw new RuntimeException();
}
String fileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
String objectName = PREFIX + fileName;
try {
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(objectName)
.stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
//文件名称相同会覆盖
minioClient.putObject(objectArgs);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return objectName;
}
/**
* 预览
* @param fileName
* @param bucketName bucketName
* @return
*/
public String preview(String bucketName,String fileName){
// 查看文件地址
GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(bucketName).object(fileName).method(Method.GET).build();
try {
String url = minioClient.getPresignedObjectUrl(build);
return url;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 文件下载
* @param fileName 文件名称
* @param bucketName bucketName
* @param res response
* @return Boolean
*/
public void download(String bucketName,String fileName, HttpServletResponse res) {
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
.object(fileName).build();
try (GetObjectResponse response = minioClient.getObject(objectArgs)){
byte[] buf = new byte[1024];
int len;
try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()){
while ((len=response.read(buf))!=-1){
os.write(buf,0,len);
}
os.flush();
byte[] bytes = os.toByteArray();
res.setCharacterEncoding("utf-8");
// 设置强制下载不打开
// res.setContentType("application/force-download");
res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
try (ServletOutputStream stream = res.getOutputStream()){
stream.write(bytes);
stream.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 查看文件对象
* @param bucketName bucketName
* @return 存储bucket内文件对象信息
*/
public List<Item> listObjects(String bucketName) {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).build());
List<Item> items = new ArrayList<>();
try {
for (Result<Item> result : results) {
items.add(result.get());
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return items;
}
/**
* 删除文件
* @param fileName
* @param bucketName
* @return
* @throws Exception
*/
public boolean remove(String bucketName,String fileName){
try {
minioClient.removeObject( RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
}catch (Exception e){
return false;
}
return true;
}
}
单元测试,引入工具类
@Autowired
private MinioUtils minioUtils;
测试新建桶
/**
* 创建存储bucket
*/
@Test
public void createBucke(){
List<Bucket> allBuckets = minioUtils.getAllBuckets();
allBuckets.stream().forEach(bucket -> System.out.println(bucket.name()));
minioUtils.makeBucket("new");
allBuckets = minioUtils.getAllBuckets();
allBuckets.stream().forEach(bucket -> System.out.println(bucket.name()));
}
测试获取全部桶
/**
* 获取全部bucket
*/
@Test
public void getAllBuckets() {
List<Bucket> allBuckets = minioUtils.getAllBuckets();
System.out.println(allBuckets);
}
测试桶是否存在
/**
* 测试桶是否存在
*/
@Test
public void bucketExists() {
System.out.println(minioUtils.bucketExists("badao"));//true
System.out.println(minioUtils.bucketExists("test"));//false
}
测试移除桶
/**
* 移除bucket
*/
@Test
public void removeBucke(){
List<Bucket> allBuckets = minioUtils.getAllBuckets();
allBuckets.stream().forEach(bucket -> System.out.println(bucket.name()));
minioUtils.removeBucket("new");
allBuckets = minioUtils.getAllBuckets();
allBuckets.stream().forEach(bucket -> System.out.println(bucket.name()));
}
测试文件上传,这里使用MockMultipartFile模拟文件
/**
* upload file
*/
@Test
public void upload(){
//MockmulpipartFile
//第一个参数是表单中文件上传的字段名
//第二个参数是文件名
//第三个参数是文件的MIME类型
//第四个参数是文件的内容
MultipartFile mockFile = new MockMultipartFile("file","badao.txt","text/plain","File content".getBytes());
minioUtils.upload("badao",mockFile);
}
Java中MockMultipartFile使用来模拟文件的用法
MockmulpipartFile
第一个参数是表单中文件上传的字段名
第二个参数是文件名
第三个参数是文件的MIME类型
第四个参数是文件的内容
上传成功查看效果,可进行预览、下载等操作。
此时到minio的存储路径中也能看到文件
测试测试Minio预览
/**
* preview file
*/
@Test
public void preview(){
String prefix = minioUtils.PREFIX;
String preview = minioUtils.preview("badao", prefix+"cd6b22b2-55ce-41ea-ba6f-a9115e12a2fe.txt");
System.out.println(preview);
}
预览效果
测试Minio下载效果
import com.ruoyi.common.utils.MinioUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
@RestController
@RequestMapping("/test/minio")
public class MinioTestController {
@Autowired
private MinioUtils minioUtils;
@GetMapping("/download")
public void download(HttpServletResponse response) {
String prefix = minioUtils.PREFIX;
minioUtils.download("badao",prefix+"cd6b22b2-55ce-41ea-ba6f-a9115e12a2fe.txt",response);
}
}
请求接口测试效果
更多功能和使用参考官方文档说明。