kubectl create ns minio
我们下面的yaml文件用到了minio
名称空间,所以需要先把该名称空间创建出来
在安装minio之前,以下几点需要先说明一下
yaml文件内容修改说明:大家只用把写注释的地方改成自己的,其他地方都不用变
安装命令:大家可以把下面yaml文件内容复制到minio.yaml
中,然后上传到linux中,之后通过kubectl apply -f minio.yaml
命令来安装minio
端口解释:对于端口部分解释一下,5000
端口是供浏览器访问UI页面的,而9000
端口是供客户端连接的
访问链接:安装完成之后,可以通过http://ip:port
来访问minio,其中ip就是虚拟机ip,而端口就是5000
端口对应的nodePort端口,比如下面yaml文件中的就是30427
登录信息:登录用户名和密码需要看yaml文件中的MINIO_ROOT_USER
和MINIO_ROOT_PASSWORD
的value值,比如我的就是admin
和admin123456
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: minio
# 名称空间
namespace: minio
spec:
# 副本数量,建议1个,集群版可能存在问题
replicas: 1
selector:
matchLabels:
app: minio
serviceName: minio
template:
metadata:
labels:
app: minio
spec:
containers:
- command:
- /bin/sh
- -c
- minio server /data --console-address ":5000"
env:
- name: MINIO_ROOT_USER
# 登录用户名,按照自己的来设置
value: "admin"
- name: MINIO_ROOT_PASSWORD
# 登录密码,按照自己的来设置
value: "admin123456"
image: minio/minio:latest
name: minio
ports:
- containerPort: 9000
name: data
protocol: TCP
- containerPort: 5000
name: console
protocol: TCP
volumeMounts:
- mountPath: /data
name: data
volumeClaimTemplates:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
spec:
accessModes:
- ReadWriteMany
resources:
requests:
# MioIO存储空间大小
storage: 5Gi
# nfs动态挂载存储类名称
storageClassName: "managed-nfs-storage-bakckup"
---
apiVersion: v1
kind: Service
metadata:
labels:
app: minio
name: minio
namespace: minio
spec:
ports:
- name: data
port: 9000
protocol: TCP
targetPort: 9000
# 暴露端口
nodePort: 30024
- name: console
port: 5000
protocol: TCP
targetPort: 5000
# 暴露端口
nodePort: 30427
selector:
app: minio
type: NodePort
登录之后效果如下图:
<?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>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.atguigu</groupId>
<artifactId>minio-study-springboot</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.0</version>
</dependency>
<!-- 注意:这个依赖必须添加,否则无法启动程序 -->
<!-- 解决报错:okhttp3.RequestBody.create([BLokhttp3/MediaType;)Lokhttp3/RequestBody -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
# minio文件上传大小限制
spring:
servlet:
multipart:
max-file-size: 1GB
max-request-size: 1GB
# minio连接配置
minio:
# minio通信地址
endpoint: http://192.168.139.128:30024
# minio登录用户名
userName: admin
# minio登录密码
password: admin123456
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties("minio")
public class MinioProperties {
/**
* 访问地址 http://localhost:9000
*/
private String endpoint;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String password;
}
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Data
@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfig {
@Resource
private MinioProperties minioProperties;
/**
* 注入minio 客户端
*/
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(minioProperties.getEndpoint())
.credentials(minioProperties.getUserName(), minioProperties.getPassword())
.build();
}
}
import lombok.Data;
@Data
public class ObjectItem {
private String objectName;
private Long size;
}
@Component
@Slf4j
public class MinioUtil {
@Resource
private MinioClient minioClient;
/**
* 判断存储桶是否存在,不存在则创建
*
* @param bucketName 存储桶名称
*/
public void existBucket(String bucketName) {
try {
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!exists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 查看存储桶是否存在
*
* @param bucketName 存储桶名称
* @return 桶是否存在
*/
public boolean bucketExists(String bucketName) {
boolean found;
try {
found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
found = false;
e.printStackTrace();
}
return found;
}
/**
* 创建存储桶
*
* @param bucketName 存储桶名称
* @return 是否创建成功
*/
public Boolean makeBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 删除存储桶
*
* @param bucketName 存储桶名称
* @return 是否删除成功
*/
public Boolean removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 文件上传
*
* @param bucketName 存储桶名称
* @param file 文件
* @return 桶中位置
*/
public String upload(String bucketName, MultipartFile file) {
MultipartFile[] fileArr = {file};
List<String> fileNames = upload(bucketName, fileArr);
return fileNames.size() == 0 ? null : fileNames.get(0);
}
/**
* 上传文件
*
* @param bucketName 存储桶名称
* @param fileList 文件列表
* @return 桶中位置列表
*/
public List<String> upload(String bucketName, List<MultipartFile> fileList) {
MultipartFile[] fileArr = fileList.toArray(new MultipartFile[0]);
return upload(bucketName, fileArr);
}
/**
* description: 上传文件
*
* @param bucketName 存储桶名称
* @param fileArr 文件列表
* @return 桶中位置列表
*/
public List<String> upload(String bucketName, MultipartFile[] fileArr) {
// 保证桶一定存在
existBucket(bucketName);
// 执行正常操作
List<String> bucketFileNames = new ArrayList<>(fileArr.length);
for (MultipartFile file : fileArr) {
// 获取桶中文件名称
// 获取原始文件名称
String originalFileName = file.getOriginalFilename();
// 获取当前日期,格式例如:2022/12-04/00,这种年/月-日/小时的文件存储方式是我们公司的文件存储方式,所以这里我也采用这种方式
String datePath = new SimpleDateFormat("yyyy/MM-dd/HH").format(new Date());
// 文件名称
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
// 获取文件后缀
String type;
int index = originalFileName.lastIndexOf(46);
if (index != -1) {
type = originalFileName.substring(index + 1);
} else {
type = "unkown";
}
String bucketFileName = datePath + "/" + uuid + "." + type;
// 推送文件到MinIO
try (InputStream in = file.getInputStream()) {
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(bucketFileName)
.stream(in, in.available(), -1)
.contentType(file.getContentType())
.build()
);
} catch (Exception e) {
e.printStackTrace();
}
bucketFileNames.add(bucketFileName);
}
return bucketFileNames;
}
/**
* 文件下载
*
* @param bucketName 存储桶名称
* @param bucketFileName 桶中文件名称
* @param originalFileName 原始文件名称
* @param response response对象
*/
public void download(String bucketName, String bucketFileName, String originalFileName, HttpServletResponse response) {
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(bucketFileName).build();
try (GetObjectResponse objResponse = minioClient.getObject(objectArgs)) {
byte[] buf = new byte[1024];
int len;
try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
while ((len = objResponse.read(buf)) != -1) {
os.write(buf, 0, len);
}
os.flush();
byte[] bytes = os.toByteArray();
response.setCharacterEncoding("utf-8");
//设置强制下载不打开
response.setContentType("application/force-download");
// 设置附件名称编码
originalFileName = new String(originalFileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
// 设置附件名称
response.addHeader("Content-Disposition", "attachment;fileName=" + originalFileName);
// 写入文件
try (ServletOutputStream stream = response.getOutputStream()) {
stream.write(bytes);
stream.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取已上传对象的文件流
*
* @param bucketName 存储桶名称
* @param bucketFileName 桶中文件名称
* @return 文件流
*/
public InputStream getFileStream(String bucketName, String bucketFileName) throws Exception {
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(bucketFileName).build();
return minioClient.getObject(objectArgs);
}
/**
* 批量删除文件对象结果
*
* @param bucketName 存储桶名称
* @param bucketFileName 桶中文件名称
* @return 删除结果
*/
public DeleteError removeObjectsResult(String bucketName, String bucketFileName) {
List<DeleteError> results = removeObjectsResult(bucketName, Collections.singletonList(bucketFileName));
return results.size() > 0 ? results.get(0) : null;
}
/**
* 批量删除文件对象结果
*
* @param bucketName 存储桶名称
* @param bucketFileNames 桶中文件名称集合
* @return 删除结果
*/
public List<DeleteError> removeObjectsResult(String bucketName, List<String> bucketFileNames) {
Iterable<Result<DeleteError>> results = removeObjects(bucketName, bucketFileNames);
List<DeleteError> res = new ArrayList<>();
for (Result<DeleteError> result : results) {
try {
res.add(result.get());
} catch (Exception e) {
e.printStackTrace();
log.error("遍历删除结果出现错误:" + e.getMessage());
}
}
return res;
}
/**
* 批量删除文件对象
*
* @param bucketName 存储桶名称
* @param bucketFileNames 桶中文件名称集合
*/
private Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> bucketFileNames) {
List<DeleteObject> dos = bucketFileNames.stream().map(DeleteObject::new).collect(Collectors.toList());
return minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
}
/**
* 查看文件对象
*
* @param bucketName 存储桶名称
* @return 文件对象集合
*/
public List<ObjectItem> listObjects(String bucketName) {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).build());
List<ObjectItem> objectItems = new ArrayList<>();
try {
for (Result<Item> result : results) {
Item item = result.get();
ObjectItem objectItem = new ObjectItem();
objectItem.setObjectName(item.objectName());
objectItem.setSize(item.size());
objectItems.add(objectItem);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return objectItems;
}
/**
* 获取桶(桶类型:public)中文件访问url
*
* @param bucketName 存储桶名称
* @param bucketFileName 桶中文件名称
* @return 访问url
*/
public String getUploadedObjectUrlForPublicBucket(String bucketName, String bucketFileName) {
return bucketName + "/" + bucketFileName;
}
/**
* 获取桶(不限制桶类型)中文件访问url
*
* @param bucketName 存储桶名称
* @param bucketFileName 桶中文件名称
* @param expiry 过期时间数量
* @param timeUnit 过期时间单位
* @return 访问url
*/
public String getUploadedObjectUrl(String bucketName, String bucketFileName, Integer expiry, TimeUnit timeUnit) {
GetPresignedObjectUrlArgs urlArgs = GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(bucketFileName)
.expiry(expiry, timeUnit)
.build();
try {
return minioClient.getPresignedObjectUrl(urlArgs);
} catch (Exception e) {
log.error("获取已上传文件的 Url 失败:" + e.getMessage());
return "";
}
}
}
import com.atguigu.miniostudyspringboot.util.MinioUtil;
import io.minio.messages.DeleteError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;
@RestController
public class StorageController {
private static final String BUCKET_NAME = "knowledge";
@Resource
private MinioUtil minIoUtil;
// 上传文件
@PostMapping("/uploadFile")
public String uploadFile(@RequestParam("file") MultipartFile file) {
return minIoUtil.upload(BUCKET_NAME, file);
}
// 删除文件
@GetMapping("/deleteFile")
public DeleteError deleteFile(@RequestParam String bucketFileName) {
return minIoUtil.removeObjectsResult(BUCKET_NAME, bucketFileName);
}
// 下载文件
@GetMapping("/downloadFile")
public void uploadFile(@RequestParam String bucketFileName, @RequestParam String originalFilename, HttpServletResponse response) {
minIoUtil.download(BUCKET_NAME, bucketFileName, originalFilename, response);
}
// 获取文件临时分享地址
@GetMapping("/shareUrl")
public String shareUrl(@RequestParam String bucketFileName) {
return minIoUtil.getUploadedObjectUrl(BUCKET_NAME, bucketFileName, 7, TimeUnit.DAYS);
}
}