springboot整合minio的超详细教程
作者:逆风飞翔的小叔
一、前言
在很多互联网产品应用中,都涉及到各种与文件存储相关的业务,随着技术的发展,关于如何解决分布式文件存储也有了比较成熟的方案,比如私有云部署下可以考虑fastdfs,阿里云对象存储oss,七牛云等,本篇将为你介绍另一种文件存储方式,即MinIO 。
二、Minio 概述
2.1 Minio简介
MinIO基于Apache License v2.0开源协议的对象存储服务,可以做为云存储的解决方案用来保存海量的图片,视频,文档,是一款高性能、分布式的对象存储系统, 可以100%的运行在标准硬件,即X86等低成本机器也能够很好的运行MinIO。
传统的存储和其他的对象存储不同的是:
它一开始就针对性能要求更高的私有云标准进行软件架构设计。因为MinIO一开始就只为对象存储而设计。所以他采用了更易用的方式进行设计,它能实现对象存储所需要的全部功能,在性能上也更加强劲,它不会为了更多的业务功能而妥协,失去MinIO的易用性、高效性。 这样的结果所带来的好处是:它能够更简单的实现局有弹性伸缩能力的原生对象存储服务。
2.2 Minio特点
Minio具有如下特点
- 性能高,准硬件条件下它能达到55GB/s的读、35GB/s的写速率;
- 部署自带管理界面;
- MinIO.Inc运营的开源项目,社区活跃度高;
- 提供了所有主流开发语言的SDK;
- 基于Golang语言实现,配置简单,单行命令可以运行起来;
- 兼容亚马逊S3云存储服务接口,适合于存储大容量非结构化的数据,一个对象文件可以是任意大小,从几kb到最大5T不等;
三、Minio 环境搭建
本文采用docker的方式快速搭建起Minio的环境,也可以通过官网下载安装包部署,官网安装包下载地址
3.1 部署过程
3.1.1 拉取镜像
docker pull minio/minio
3.1.2 启动容器
docker run -d -p 9000:9000 -p 9090:9090 \ --name minio \ -e "MINIO_ACCESS_KEY=minio" \ -e "MINIO_SECRET_KEY=minio" \ -v /home/minio/data:/data \ -v /home/minio/config:/root/.minio \ minio/minio server \ /data --console-address ":9090" -address ":9000"
3.1.3 访问web页面
容器启动成功后,注意开发相关的防火墙端口即可,然后访问地址:IP:9000,即可访问Minio的web界面
输入账户和密码,登录进去之后,看到下面的效果说明Minio环境搭建完成
四、Minio基本使用
4.1 基本概念
在正式开始使用Minio之前,有必要先了解下几个相关的概念
- bucket ,类比于文件系统的目录;
- Object ,类比文件系统的文件;
- Keys ,类比文件名;
4.2 上传文件演示
如下,点击创建一个新的bucket,创建完成后就可以在列表上看到这个bucket;
给当前这个bucket上传一个文件
点击文件夹图标
上传一张本地文件,上传完成后就可以看到这个文件了,也可以浏览上传的文件
4.3 用户管理
针对客户端的操作,经常需要维护相关的账号来管理,比如账户的操作权限,访问控制等;
点击,创建用户
填写用户信息,勾选权限保存即可
然后在用户列表就可以看到这个用户了
4.4 Java操作Minio
通过上面的环境搭建和操作,演示并了解了如何快速使用Minio,更多的功能可以参阅相关资料进一步学习了解,下面我们编写java代码完成文件的上传。
4.4.1 导入依赖
在当前的maven工程中导入minio的依赖,客户端具体版本可以根据你的实际需要选择
<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>7.1.0</version> </dependency>
4.4.2 上传文件到minio
通过下面这段代码将本地的一张图片文件上传到minio的test这个bucket目录下
public static void main(String[] args) { FileInputStream inputStream = null; try { inputStream = new FileInputStream("F:\\网盘\\Title-logo.png"); MinioClient client = MinioClient.builder().credentials("minio", "minio").endpoint("http://IP:9000").build(); PutObjectArgs putObjectArgs = PutObjectArgs.builder() .object("logo.png") .contentType("image/png") .bucket("test") .stream(inputStream, inputStream.available(), -1) .build(); client.putObject(putObjectArgs); System.out.println("上传成功"); } catch (Exception e) { e.printStackTrace(); } finally { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
运行这段代码,上传成功后,去到test的目录下刷新之后可以看到文件已经上传
五、springboot整合Minio
接下来让我们看看如何在springboot中集成Minio,参考下面的操作步骤
5.1 前置准备
5.1.1 引入依赖
创建一个maven工程,引入如下相关的依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>7.1.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies>
5.1.2 核心配置文件
在配置文件中添加如下内容
server: port: 8087 logging: level: root: info com.congge.model: debug minio: username: minio pwd: minio bucket: test endpoint: http://IP:9000 readPath: http://IP:9000
5.2 编码过程
5.2.1 创建一个参数配置类
通过这种方式将配置文件中以minio开头的那些配置加载到spring容器中管理,其他位置使用的时候直接注入即可
@Data @ConfigurationProperties(prefix = "minio") @Component public class MinIOConfigProperties implements Serializable { private String username; private String pwd; private String bucket; private String endpoint; private String readPath; }
5.2.2 创建minio配置类
通过这个全局的配置类,其他需要上传文件的类中直接注入MinioClient即可
import io.minio.MinioClient; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Data @Configuration public class MinIOConfig { @Autowired private MinIOConfigProperties minIOConfigProperties; @Bean public MinioClient buildMinioClient() { return MinioClient .builder() .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey()) .endpoint(minIOConfigProperties.getEndpoint()) .build(); } }
5.2.3 创建minio文件服务类或工具类
在日常开发中,可以自定义一个minio的工具类,使用的时候就比较方便了,下面的代码中列举了常用的一些API操作方法,可以结合实际需要自定义更多的工具方法
import com.congge.config.MinIOConfig; import com.congge.config.MinIOConfigProperties; import com.congge.service.MinioFileService; import io.minio.*; import io.minio.messages.Bucket; import io.minio.messages.Item; import lombok.extern.slf4j.Slf4j; import org.apache.poi.util.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; @Slf4j @Import(MinIOConfig.class) @Service public class MinioFileServiceImpl implements MinioFileService { @Autowired private MinioClient minioClient; @Autowired private MinIOConfigProperties minIOConfigProperties; private final static String separator = "/"; /** * 下载文件 * * @param pathUrl 文件全路径 * @return 文件流 */ @Override public void downLoadFile(String pathUrl, HttpServletResponse response) { String[] pathItems = pathUrl.split("/"); String originFileName = pathItems[pathItems.length - 1]; String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", ""); int index = key.indexOf(separator); //String bucket = key.substring(0,index); String filePath = key.substring(index + 1); InputStream inputStream = null; try { inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build()); response.setHeader("Content-Disposition", "attachment;filename=" + originFileName); response.setContentType("application/force-download"); response.setCharacterEncoding("UTF-8"); IOUtils.copy(inputStream, response.getOutputStream()); System.out.println("下载成功"); } catch (Exception e) { log.error("minio down file error. pathUrl:{}", pathUrl); e.printStackTrace(); } finally { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } @Override public String uploadFile(MultipartFile file) throws Exception { String bucketName = minIOConfigProperties.getBucket(); String endpoint = minIOConfigProperties.getEndpoint(); // 检查存储桶是否已经存在 boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); if (isExist) { System.out.println("Bucket already exists."); } else { minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); } String originalFilename = file.getOriginalFilename(); //拼接生成新的UUID形式的文件名 String objectName = new SimpleDateFormat("yyyy/MM/dd/").format(new Date()) + UUID.randomUUID().toString().replaceAll("-", "") + originalFilename.substring(originalFilename.lastIndexOf(".")); PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName) .bucket(bucketName) .contentType(file.getContentType()) .stream(file.getInputStream(), file.getSize(), -1).build(); minioClient.putObject(objectArgs); //组装桶中文件的访问url String resUrl = endpoint + "/" + bucketName + "/" + objectName; return resUrl; } /** * 删除文件 * * @param pathUrl 文件全路径 */ @Override public void delete(String pathUrl) { String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", ""); int index = key.indexOf(separator); String bucket = key.substring(0, index); String filePath = key.substring(index + 1); // 删除Objects RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build(); try { minioClient.removeObject(removeObjectArgs); } catch (Exception e) { log.error("minio remove file error. pathUrl:{}", pathUrl); e.printStackTrace(); } } public List<Bucket> listBuckets() throws Exception { return minioClient.listBuckets(); } public boolean bucketExists(String bucketName) throws Exception { boolean flag = minioClient.bucketExists(bucketName); if (flag) { return true; } return false; } @Override public List<String> listBucketNames() throws Exception{ List<Bucket> bucketList = listBuckets(); List<String> bucketListName = new ArrayList<>(); for (Bucket bucket : bucketList) { bucketListName.add(bucket.name()); } return bucketListName; } @Override public boolean makeBucket(String bucketName) throws Exception{ boolean flag = bucketExists(bucketName); if (!flag) { minioClient.makeBucket(bucketName); return true; } else { return false; } } @Override public boolean removeBucket(String bucketName) throws Exception{ boolean flag = bucketExists(bucketName); if (flag) { Iterable<Result<Item>> myObjects = listObjects(bucketName); for (Result<Item> result : myObjects) { Item item = result.get(); // 有对象文件,则删除失败 if (item.size() > 0) { return false; } } // 删除存储桶,注意,只有存储桶为空时才能删除成功。 minioClient.removeBucket(bucketName); flag = bucketExists(bucketName); if (!flag) { return true; } } return false; } @Override public List<String> listObjectNames(String bucketName) throws Exception{ List<String> listObjectNames = new ArrayList<>(); boolean flag = bucketExists(bucketName); if (flag) { Iterable<Result<Item>> myObjects = listObjects(bucketName); for (Result<Item> result : myObjects) { Item item = result.get(); listObjectNames.add(item.objectName()); } } return listObjectNames; } @Override public boolean removeObject(String bucketName, String objectName) throws Exception{ boolean flag = bucketExists(bucketName); if (flag) { List<String> objectList = listObjectNames(bucketName); for (String s : objectList) { if(s.equals(objectName)){ minioClient.removeObject(bucketName, objectName); return true; } } } return false; } @Override public String getObjectUrl(String bucketName, String objectName) throws Exception{ boolean flag = bucketExists(bucketName); String url = ""; if (flag) { url = minioClient.getObjectUrl(bucketName, objectName); } return url; } public Iterable<Result<Item>> listObjects(String bucketName) throws Exception { boolean flag = bucketExists(bucketName); if (flag) { return minioClient.listObjects(bucketName); } return null; } }
5.2.4 编写测试接口
为了方便测试,下面定义了一个测试接口
import com.congge.service.MinioFileService; 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.HttpServletResponse; import java.util.List; @RestController @Slf4j public class FileController { @Autowired private MinioFileService minioFileService; /** * 上传文件 * @param file * @return * @throws Exception */ @PostMapping("/upload") public String upload(@RequestBody MultipartFile file) throws Exception { String url = minioFileService.uploadFile(file); return "文件上传成功,文件路径:" + url; } /** * 下载文件 * @param pathUrl * @param response */ @GetMapping("/download") public void download(@RequestParam("pathUrl") String pathUrl, HttpServletResponse response) { minioFileService.downLoadFile(pathUrl,response); } /** * 列出所有bucket名称 * @return * @throws Exception */ @PostMapping("/list/bucket") public List<String> list() throws Exception { return minioFileService.listBucketNames(); } /** * 创建bucket * @param bucketName * @return * @throws Exception */ @PostMapping("/create/bucket") public boolean createBucket(@RequestParam("bucketName")String bucketName) throws Exception { return minioFileService.makeBucket(bucketName); } /** * 删除bucket * @param bucketName * @return * @throws Exception */ @PostMapping("/delete/bucket") public boolean deleteBucket(@RequestParam("bucketName")String bucketName) throws Exception { return minioFileService.removeBucket(bucketName); } /** * 列出bucket的所有对象名称 * @param bucketName * @return * @throws Exception */ @PostMapping("/list/object_names") public List<String> listObjectNames(@RequestParam("bucketName")String bucketName) throws Exception { return minioFileService.listObjectNames(bucketName); } /** * 删除bucket中的某个对象 * @param bucketName * @param objectName * @return * @throws Exception */ @PostMapping("/remove/object") public boolean removeObject(@RequestParam("bucketName")String bucketName,@RequestParam("objectName") String objectName) throws Exception { return minioFileService.removeObject(bucketName, objectName); } /** * 获取文件访问路径 * @param bucketName * @param objectName * @return * @throws Exception */ @PostMapping("/get/object/url") public String getObjectUrl(@RequestParam("bucketName")String bucketName, @RequestParam("objectName")String objectName) throws Exception { return minioFileService.getObjectUrl(bucketName, objectName); } }
5.2.5 接口测试
上传文件测试
上传成功后,返回了文件的完整路径,方便后续使用
然后在控制台的test这个bucket中检查是否上传上去了
下载文件测试
使用上一步返回的url,直接调用下载接口,下载完成后可以直接预览
更多的接口有兴趣的同学可以一一尝试下,就不再赘述了。
六、写在文末
本文详细总结了Minio的搭建使用,以及与springboot整合的完整步骤,作为一款适用且轻量级的文件存储服务器,可以私有化部署,也可以很方便进行云上部署、容器化部署,为今后在实际项目中的技术选型提供一个参考,本篇到此结束,感谢观看。
以上就是springboot整合minio的超详细教程的详细内容,更多关于springboot整合minio的资料请关注脚本之家其它相关文章!