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 {@Autowiredprivate MinioProperties properties;@Beanpublic 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 {@Autowiredprivate MinioProperties prop;@Autowiredprivate MinioConfiguration minioConfiguration;@Resourceprivate 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实现文件上传!