官方文档地址
https://docs.min.io/
http://docs.minio.org.cn/docs/
JavaSDK: https://github.com/minio/minio-java
什么是 MinIO?
Minio 是个基于 Golang 编写的开源对象存储套件,基于Apache License v2.0开源协议,虽然轻量,却拥有着不错的性能。它兼容亚马逊S3云存储服务接口。可以很简单的和其他应用结合使用,例如 NodeJS、Redis、MySQL等。
MinIO 的应用场景
MinIO 的应用场景除了可以作为私有云的对象存储服务来使用,也可以作为云对象存储的网关层,无缝对接 Amazon S3
或者 MicroSoft Azure
。
minio特点
-
高性能:
作为一款高性能存储,在标准硬件条件下,其读写速率分别可以达到55Gb/s
和35Gb/s
。并而 MinIO 支持一个对象文件可以是任意大小,从几kb到最大5T不等。 -
可扩展:
不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并且支持跨越多个数据中心。 -
云原生:
容器化、基于K8S的编排、多租户支持。 -
Amazon S3兼容:
使用 Amazon S3 v2 / v4 API。可以使用Minio SDK,Minio Client,AWS SDK 和 AWS CLI 访问Minio服务器。 -
可对接多种后端存储:
除了Minio自己的文件系统,还支持 DAS、 JBODs、NAS、Google云存储和 Azure Blob存储。
window安装minio服务(本地)
1.下载地址
https://dl.min.io/server/minio/release/windows-amd64/minio.exe
2.进入下载目录,调用cmd窗口,并执行如下命令:
.\minio.exe server F:\minio\photos
3.访问:http://127.0.0.1:9000/,进入minio服务器登录页面
默认账号密码均为:minioadmin
minio之java应用
1.pom引入jar包依赖
<!-- minio 文件服务客户端 --> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>6.0.11</version> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.1.1</version> </dependency>
2.添加配置文件
- url:minio服务器的接口地址,不是访问地址
- accessKey/secretKey:登录minio系统,新建Service Accounts
config: minio: url: http://192.168.1.114:9000 accessKey: MMXHB6HAXWCCVG8ULAVK secretKey: 7J7v99N0LubbzshG2UmOrID9j2PNMG5mpCnBENv1
3.注册MinioClient
package cn.cjq.config; import io.minio.MinioClient; import lombok.Data; import lombok.SneakyThrows; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; //import org.springframework.cloud.context.config.annotation.RefreshScope; @Data @Configuration @ConfigurationProperties(prefix = "config.minio") public class MinioConfig { /** * minio 服务地址 http://ip:port */ private String url; /** * 用户名 */ private String accessKey; /** * 密码 */ private String secretKey; @SneakyThrows @Bean // @RefreshScope public MinioClient minioClient(){ return new MinioClient(url, accessKey, secretKey); } }
4.minio工具类
package cn.cjq.util; import cn.cjq.entity.MinioItem; import io.minio.MinioClient; import io.minio.ObjectStat; import io.minio.Result; import io.minio.messages.Bucket; import io.minio.messages.Item; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * minio工具类 * * @author lvlinguang * @date 2022-01-07 12:26 * @see “http://docs.minio.org.cn/docs/master/java-client-api-reference” */ @Component public class MinioUtils { @Autowired private MinioClient client; /** * 创建bucket * * @param bucketName */ @SneakyThrows public void createBucket(String bucketName) { if (!client.bucketExists(bucketName)) { client.makeBucket(bucketName); } } /** * 获取所有bucket * * @return */ @SneakyThrows public List<Bucket> listAllBuckets() { return client.listBuckets(); } /** * bucket详情 * * @param bucketName 名称 * @return */ @SneakyThrows public Optional<Bucket> getBucket(String bucketName) { return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst(); } /** * 删除bucket * * @param bucketName 名称 */ @SneakyThrows public void removeBucket(String bucketName) { client.removeBucket(bucketName); } /** * 上传文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @param stream 文件流 * @throws Exception */ @SneakyThrows public void uploadFile(String bucketName, String objectName, InputStream stream) throws Exception { this.uploadFile(bucketName, objectName, stream, (long) stream.available(), "application/octet-stream"); } /** * 上传文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @param stream 文件流 * @param size 大小 * @param contextType 类型 * @throws Exception */ @SneakyThrows public void uploadFile(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception { //如果bucket不存在,则创建 this.createBucket(bucketName); client.putObject(bucketName, objectName, stream, size, null, null, contextType); } /** * 获取文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @return */ @SneakyThrows public InputStream getFile(String bucketName, String objectName) { return client.getObject(bucketName, objectName); } /** * 根据文件前置查询文件 * * @param bucketName bucket名称 * @param prefix 前缀 * @param recursive 是否递归查询 * @return */ @SneakyThrows public List<MinioItem> listAllFileByPrefix(String bucketName, String prefix, boolean recursive) { List<MinioItem> objectList = new ArrayList<>(); Iterable<Result<Item>> objectsIterator = client .listObjects(bucketName, prefix, recursive); while (objectsIterator.iterator().hasNext()) { objectList.add(new MinioItem(objectsIterator.iterator().next().get())); } return objectList; } /** * 删除文件 * * @param bucketName bucket名称 * @param objectName 文件名称 */ @SneakyThrows public void removeFile(String bucketName, String objectName) { client.removeObject(bucketName, objectName); } /** * 获取文件外链 * * @param bucketName bucket名称 * @param objectName 文件名称 * @param expires 过期时间 <=7 * @return */ @SneakyThrows public String getFileURL(String bucketName, String objectName, Integer expires) { return client.presignedGetObject(bucketName, objectName, expires); } /** * 获取文件信息 * * @param bucketName bucket名称 * @param objectName 文件名称 * @return */ @SneakyThrows public ObjectStat getFileInfo(String bucketName, String objectName) { return client.statObject(bucketName, objectName); } }View Code
5.文件操作类
package cn.cjq.util; import cn.cjq.entity.MinioObject; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.time.LocalDate; import java.util.UUID; /** * 系统文件工具类 * * @author lvlinguang * @date 2021-02-28 12:30 */ @Component public class SysFileUtils { /** * 文件服务器中的目录分隔符 */ public static final String SEPRETOR = "/"; @Autowired private MinioUtils minioUtils; /** * 文件上传 * * @param object 文件对你 * @param bucketName bucket名称 * @return */ @SneakyThrows public MinioObject uploadFile(MultipartFile object, String bucketName) { return this.uploadFile(object.getInputStream(), bucketName, object.getOriginalFilename()); } /** * 文件上传 * * @param object 文件对你 * @param bucketName bucket名称 * @param fileName 文件名 * @return */ @SneakyThrows public MinioObject uploadFile(MultipartFile object, String bucketName, String fileName) { return this.uploadFile(object.getInputStream(), bucketName, fileName); } /** * 文件上传 * * @param object 文件对你 * @param bucketName bucket名称 * @param randomFileName 文件名是否随机(是:按年/月/日/随机值储存,否:按原文件名储存) * @return */ @SneakyThrows public MinioObject uploadFile(MultipartFile object, String bucketName, Boolean randomFileName) { //文件名 String fileName = object.getOriginalFilename(); if (randomFileName) { //扩展名 String extName = FileUtil.extName(object.getOriginalFilename()); if (StrUtil.isNotBlank(extName)) { extName = StrUtil.DOT + extName; } //新文件名 fileName = randomFileName(extName); } return this.uploadFile(object.getInputStream(), bucketName, fileName); } /** * 文件上传 * * @param object 文件对你 * @param bucketName bucket名称 * @param fileName 文件名 * @return */ @SneakyThrows public MinioObject uploadFile(InputStream object, String bucketName, String fileName) { try { minioUtils.uploadFile(bucketName, fileName, object); return new MinioObject(minioUtils.getFileInfo(bucketName, fileName)); } catch (Exception e) { throw new Exception(e); } finally { if (object != null) { object.close(); } } } /** * 下载文件 * * @param response response * @param url 文件地址(/bucketName/fileName) */ public void downloadFile(HttpServletResponse response, String url) { final String bucketName = getBucketName(url); final String filePath = getFilePath(url); this.downloadFile(response, bucketName, filePath); } /** * 下载文件 * * @param response response * @param bucket bucket名称 * @param fileName 文件名 */ public void downloadFile(HttpServletResponse response, String bucket, String fileName) { try (InputStream inputStream = minioUtils.getFile(bucket, fileName)) { if ("jpg".equals(FileUtil.extName(fileName))) { response.setContentType("image/jpeg"); } else if ("png".equals(FileUtil.extName(fileName))) { response.setContentType("image/png"); } else { response.setContentType("application/octet-stream; charset=UTF-8"); } IoUtil.copy(inputStream, response.getOutputStream()); } catch (Exception e) { e.printStackTrace(); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); } } /** * 获取链接地址的 文件名 * * @param bucketFileUrl * @return */ public static String getFilePath(String bucketFileUrl) { if (bucketFileUrl == null) { return null; } //去掉第一个分割符 if (bucketFileUrl.startsWith(SEPRETOR)) { bucketFileUrl = bucketFileUrl.substring(1); } return bucketFileUrl.substring(bucketFileUrl.indexOf(SEPRETOR) + 1); } /** * 获取链接地址的 bucketName * * @param bucketFileUrl 地址(/{bucketName}/{path}/{fileName}) * @return */ public static String getBucketName(String bucketFileUrl) { if (bucketFileUrl == null) { return null; } //去掉第一个分割符 if (bucketFileUrl.startsWith(SEPRETOR)) { bucketFileUrl = bucketFileUrl.substring(1); } return bucketFileUrl.substring(0, bucketFileUrl.indexOf(SEPRETOR)); } /** * 生成新的文件名 * * @param extName 扩展名 * @return */ public static String randomFileName(String extName) { LocalDate now = LocalDate.now(); return now.getYear() + SEPRETOR + getFullNumber(now.getMonthValue()) + SEPRETOR + getFullNumber(now.getDayOfMonth()) + SEPRETOR + UUID.randomUUID().toString().replace("-", "") + extName; } /** * 得到数字全称,带0 * * @param number * @return */ public static String getFullNumber(Integer number) { if (number < 10) { return "0" + number; } return number.toString(); } }View Code
6.相关实体类
package cn.cjq.entity; import io.minio.messages.Item; import io.minio.messages.Owner; import lombok.AllArgsConstructor; import lombok.Data; import java.util.Date; /** * @author Kartist */ @Data @AllArgsConstructor public class MinioItem { private String objectName; private Date lastModified; private String etag; private Long size; private String storageClass; private Owner owner; private String type; public MinioItem(Item item) { this.objectName = item.objectName(); this.lastModified = item.lastModified(); this.etag = item.etag(); this.size = (long) item.size(); this.storageClass = item.storageClass(); this.owner = item.owner(); this.type = item.isDir() ? "directory" : "file"; } }View Code
package cn.cjq.entity; import io.minio.ObjectStat; import lombok.Data; import java.util.Date; @Data public class MinioObject { private String bucketName; private String name; private Date createdTime; private Long length; private String etag; private String contentType; public MinioObject(ObjectStat os) { this.bucketName = os.bucketName(); this.name = os.name(); this.createdTime = os.createdTime(); this.length = os.length(); this.etag = os.etag(); this.contentType = os.contentType(); } }View Code
7.contrller入口
package cn.cjq.controller; import cn.cjq.entity.MinioObject; import cn.cjq.util.SysFileUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @RestController @RequestMapping("/hello") @Slf4j public class HelloWorldController { @Autowired private SysFileUtils sysFileUtils; /** * minio文件上传 * * @param file * @param bucketName * @return */ @PostMapping("/upload") public MinioObject upload(@RequestPart("file") MultipartFile file, @RequestParam("bucketName") String bucketName) { final MinioObject minioObject = sysFileUtils.uploadFile(file, bucketName, true); return minioObject; } /** * minio文件下载 * * @return */ @GetMapping("/info/**") public void getFile(HttpServletRequest request, HttpServletResponse response) { final String uri = request.getRequestURI(); String fullName = uri.replace("/hello/info", ""); sysFileUtils.downloadFile(response, fullName); } }
8.post测试minio文件上传
192.168.1.114:8080/hello/upload
8.浏览器下载图片
url+bucketName+name:
http://192.168.1.114:8080/hello/info/cjq/2022/01/11/1106ab4078b2496699f93c0af971c737.png
参考文献:
https://www.cnblogs.com/lvlinguang/p/15774612.html
https://www.cnblogs.com/ssgeek/p/11072053.html
https://www.jianshu.com/p/f39e7255805b