1. MinIO快速入门
1.1.MinIO核心概念
下面介绍MinIO中的几个核心概念,这些概念在所有的对象存储服务中也都是通用的。
-
对象(Object)
对象是实际的数据单元,例如我们上传的一个图片。
-
存储桶(Bucket)
存储桶是用于组织对象的命名空间,类似于文件夹。每个存储桶可以包含多个对象。
-
端点(Endpoint)
端点是MinIO服务器的网络地址,用于访问存储桶和对象,例如
http://192.168.xxx.xxx:9000
注意:
9000
为MinIO的API的默认端口,前边配置的9001
以为管理页面端口。 -
Access Key 和 Secret Key
Access Key是用于标识和验证访问者身份的唯一标识符,相当于用户名。
Secret Key是与Access Key关联的密码,用于验证访问者的身份。
1.2.MinIO控制台页面操作
前提:需要把你的MinIO部署在linux系统上,具体部署过程可看:MinIO官方
1.登录
管理页面的http://192.168.xxx.xxx:9001,登录的用户名和密码为部署时在EnvironmentFile
文件中配置的如下参数:
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin
2.创建存储桶
3.上传图片
4.访问图片
图片URL
由于MinIO提供了HTTP访问功能,所以可以通过浏览器直接访问对象。对象URL为MinIO的Endpoint
+对象的存储路径
,例如下图中的图片对象的URL为
192.168.xxx.xxx:9000/lease/a.jpg
访问权限
不出意外的话,使用浏览器访问上述URL,会得到如下响应,很显然是没有访问权限。
若想继续访问图片,需要修改图片所在桶的访问权限,如下图所示
如上图所示,可选的访问权限共有三个选项,分别是Private
、Public
和Custom
,具体说明如下
-
Private
只允许桶的所有者对该桶进行读写。
-
Public
允许所有人对该桶进行读写。
-
Custom
自定义访问权限。
若想将权限设置为只允许所有者写,但允许所有人读,就需要自定义访问权限。自定义访问权限,需要使用一个规定格式的JSON字符串进行描述,具体格式可参考官方文档。
例如以下JSON字符串表达的含义是:允许(Allow
)所有人(*
)读取(s3:GetObject
)指定桶(lease)的所有内容。
{
"Statement" : [ {
"Action" : "s3:GetObject",
"Effect" : "Allow",
"Principal" : "*",
"Resource" : "arn:aws:s3:::lease/*"
} ],
"Version" : "2012-10-17"
}
重新访问:
192.168.xxx.xxx:9000/lease/a.jpg
2. 图片上传接口开发
2.1. Minio相关配置
下面为该接口的具体实现
@ConfigurationProperties(prefix = "minio")
@Data
public class MinioProperties {
private String endpoint;
private String accessKey;
private String secretKey;
private String bucketName;
}
-
配置Minio Client
-
引入Minio Maven依赖
-
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
</dependency>
-
配置Minio相关参数
在
application.yml
中配置Minio的endpoint
、accessKey
、secretKey
、bucketName
等参数
注意:上述minio: endpoint: http://<hostname>:<port> access-key: <access-key> secret-key: <secret-key> bucket-name: <bucket-name>
<hostname>
、<port>
等信息需根据实际情况进行修改。
新建一个MinioProperties类:
@ConfigurationProperties(prefix = "minio")
@Data
public class MinioProperties {
private String endpoint;
private String accessKey;
private String secretKey;
private String bucketName;
}
新建一个MinioConfiguration类:
@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfiguration {
@Autowired
private MinioProperties properties;
@Bean
public MinioClient minioClient() {
return MinioClient.builder().endpoint(properties.getEndpoint()).credentials(properties.getAccessKey(), properties.getSecretKey()).build();
}
}
新建一个工具类:MinioUtil
package com.gjh.lease.common.minio;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.multipart.MultipartFile;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Component
@Slf4j
public class MinioUtil {
@Autowired
private MinioProperties prop;
@Autowired
private MinioConfiguration minioConfiguration;
@Resource
private MinioClient minioClient;
/**
* 查看存储bucket是否存在
* @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
* @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
* @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 文件
* @return Boolean
*/
public String upload(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
if (StringUtils.isBlank(originalFilename)){
throw new RuntimeException();
}
String fileName = new SimpleDateFormat("yyyyMMdd").format(new Date()) +
"/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
// String objectName = CommUtils.getNowDateLongStr("yyyy-MM/dd") + "/" + fileName;
String objectName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + "/" + fileName;
try {
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(prop.getBucketName()).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
* @return
*/
public String preview(String fileName){
// 查看文件地址
GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(prop.getBucketName()).object(fileName).method(Method.GET).build();
try {
String url = minioClient.getPresignedObjectUrl(build);
return url;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 文件下载
* @param fileName 文件名称
* @param res response
* @return Boolean
*/
public void download(String fileName, HttpServletResponse res) {
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName())
.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();
}
}
/**
* 查看文件对象
* @return 存储bucket内文件对象信息
*/
public List<Item> listObjects() {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(prop.getBucketName()).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
* @return
* @throws Exception
*/
public boolean remove(String fileName){
try {
minioClient.removeObject( RemoveObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build());
}catch (Exception e){
return false;
}
return true;
}
}
2.2.开发图片上传接口
controller:
@Operation(summary = "上传文件")
@PostMapping("upload")
public Result<String> upload(@RequestParam MultipartFile file) throws Exception {
String objectName = minioUtil.upload(file);
if(null != objectName){
return Result.ok(properties.getEndpoint() + "/" +properties.getBucketName() + "/" + objectName);
}
return Result.fail();
}
这样就可以实现文件上传了。
2.3.测试
实现了基于Minio实现文件上传!