• minio分布式文件存储


    基本介绍

    什么是 MinIO
            MinIO 是一款基于 Go 语言的高性能、可扩展、云原生支持、操作简单、开源的分布式对象存储产品。基于 Apache License v2.0 开源协议,虽然轻量,却拥有着不错的性能。它兼容亚马逊S3云存储服务接口。可以很简单的和其他应用结合使用,例如 NodeJS、Redis、MySQL 等。

    官网:www.minio.org.cn/

    MinIO 特点

    • 高性能:作为高性能对象存储,在标准硬件条件下它能达到55GB/s的读、35GG/s的写速率
    • 可扩容:不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并跨越多个数据中心
    • 云原生:容器化、基于K8S的编排、多租户支持
    • Amazon S3兼容:Minio使用Amazon S3 v2 / v4 API。可以使用Minio SDK,Minio Client,AWS SDK和AWS CLI访问Minio服务器。
    • 可对接后端存储: 除了Minio自己的文件系统,还支持DAS、 JBODs、NAS、Google云存储和Azure Blob存储。
    • SDK支持: 基于Minio轻量的特点,它得到类似Java、Python或Go等语言的sdk支持
    • Lambda计算: Minio服务器通过其兼容AWS SNS / SQS的事件通知服务触发Lambda功能。支持的目标是消息队列,如Kafka,NATS,AMQP,MQTT,Webhooks以及Elasticsearch,Redis,Postgres和MySQL等数据库。
    • 有操作页面
    • 功能简单: 这一设计原则让MinIO不容易出错、更快启动
    • 支持纠删码:MinIO使用纠删码、Checksum来防止硬件错误和静默数据污染。在最高冗余度配置下,即使丢失1/2的磁盘也能恢复数据

    docker 安装 minio

    下载镜像(最新版本)
    docker pull minio/minio
    
    查看已经下载的镜像
    docker images

    创建并启动minIO容器:
    这里的9090端口指的是minio的客户端端口。虽然设置9090,但是我们在访问9000的时候,他也会自动跳到9090。
    9000端口是minio的服务端端口,我们程序在连接minio的时候,就是通过这个端口来连接的。
    -v就是docker run当中的挂载,这里的/usr/local/mydata/minio/data:/data意思就是将容器的/data目录和宿主机的/mydata/minio/data目录做映射,这样我们想要查看容器的文件的时候,就不需要看容器当中的文件了。
    注意在执行命令的时候,他是会自动在宿主机当中创建目录的。我们不需要手动创建。
    minio所上传的文件默认都是存储在容器的data目录下的!
    假如删除容器了宿主机当中挂载的目录是不会删除的。假如没有使用-v挂载目录,那他在宿主机的存储位置的文件会直接删除的。
    宿主机的挂载目录一定是根目录,如果是相对路径会有问题。还有容器当中的目录也是必须是绝对路径(根路径就是带/的)。
    所谓的挂载其实就是将容器目录和宿主机目录进行绑定了,操作宿主机目录,容器目录也会变化,操作容器目录,宿主机目录也会变化。这样做的目的 可以间接理解为就是数据持久化,防止容器误删,导致数据丢失的情况。
    MINIO_ACCESS_KEY:账号 MINIO_SECRET_KEY:密码 (正常账号应该不低于3位,密码不低于8位,不然容器会启动不成功)
    –console-address 指定客户端端口
    -d --restart=always 代表重启linux的时候容器自动启动
    –name minio 容器名称

     docker run -p 9000:9000 -p 9090:9090  --name minio  -d --restart=always  

    -e "MINIO_ACCESS_KEY=minioadmin"  
    -e "MINIO_SECRET_KEY=minioadmin"  

    -v /usr/local/mydata/minio/data:/data  

    minio/minio server  /data --console-address ":9090" -address ":9000"

    springboot集成minio

    一、配置yml文件

    1. #yml配置文件增加如下配置
    2. minio:
    3.   endpoint: http://ip:9000
    4.   fileUploadUrl: http://ip:9000
    5.   accessKey: minioadmin (用户名)
    6.   secretKey: minioadmin (密码)
    7.   bucketName: file (文件组:桶名)

    二、引入pom(8与7的方法不适用,以下使用8.0.3)

    1.        
    2.             <groupId>io.miniogroupId>
    3.             <artifactId>minioartifactId>
    4.             <version>8.0.3version>
    5.         

    三、系统加载minio配置

    1. @Data
    2. @Configuration
    3. @ConfigurationProperties(prefix = "minio")
    4. public class MinioConfig {
    5. private String endpoint;
    6. private String accessKey;
    7. private String secretKey;
    8. private String bucketName;
    9. @Bean
    10. public MinioClient minioClient() {
    11. MinioClient minioClient = MinioClient.builder()
    12. .endpoint(endpoint)
    13. .credentials(accessKey, secretKey)
    14. .build();
    15. return minioClient;
    16. }
    17. }

    四、minio工具类(上传文件,返回访问链接)

    1. package org;
    2. import cn.hutool.core.io.FastByteArrayOutputStream;
    3. import com.alibaba.fastjson.JSONObject;
    4. import io.minio.*;
    5. import io.minio.http.Method;
    6. import io.minio.messages.Bucket;
    7. import io.minio.messages.DeleteError;
    8. import io.minio.messages.DeleteObject;
    9. import io.minio.messages.Item;
    10. import org.springframework.stereotype.Component;
    11. import org.springframework.web.multipart.MultipartFile;
    12. import javax.annotation.Resource;
    13. import javax.servlet.ServletOutputStream;
    14. import javax.servlet.http.HttpServletResponse;
    15. import java.io.InputStream;
    16. import java.util.ArrayList;
    17. import java.util.HashMap;
    18. import java.util.List;
    19. import java.util.UUID;
    20. import java.util.stream.Collectors;
    21. /**
    22. * 功能描述 文件服务器工具类
    23. *
    24. * @author:
    25. * @date:
    26. */
    27. @Component
    28. public class MinioUtil {
    29. @Resource
    30. private MinioClient minioClient;
    31. /**
    32. * 查看存储bucket是否存在
    33. * @return boolean
    34. */
    35. public Boolean bucketExists(String bucketName) {
    36. Boolean found;
    37. try {
    38. found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    39. } catch (Exception e) {
    40. //e.printStackTrace();
    41. return false;
    42. }
    43. return found;
    44. }
    45. /**
    46. * 创建存储bucket
    47. * @return Boolean
    48. */
    49. public Boolean makeBucket(String bucketName) {
    50. try {
    51. minioClient.makeBucket(MakeBucketArgs.builder()
    52. .bucket(bucketName)
    53. .build());
    54. } catch (Exception e) {
    55. e.printStackTrace();
    56. return false;
    57. }
    58. return true;
    59. }
    60. /**
    61. * 删除存储bucket
    62. * @return Boolean
    63. */
    64. public Boolean removeBucket(String bucketName) {
    65. try {
    66. minioClient.removeBucket(RemoveBucketArgs.builder()
    67. .bucket(bucketName)
    68. .build());
    69. } catch (Exception e) {
    70. e.printStackTrace();
    71. return false;
    72. }
    73. return true;
    74. }
    75. /**
    76. * 获取全部bucket
    77. */
    78. public List getAllBuckets() {
    79. try {
    80. return minioClient.listBuckets();
    81. } catch (Exception e) {
    82. e.printStackTrace();
    83. }
    84. return null;
    85. }
    86. /**
    87. * 文件上传
    88. * @param file 文件
    89. * @return Boolean
    90. */
    91. public Boolean upload(String bucketName, String fileName, MultipartFile file, InputStream inputStream) {
    92. try {
    93. PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(fileName)
    94. .stream(inputStream,file.getSize(),-1).contentType(file.getContentType()).build();
    95. //文件名称相同会覆盖
    96. minioClient.putObject(objectArgs);
    97. } catch (Exception e) {
    98. e.printStackTrace();
    99. return false;
    100. }
    101. return true;
    102. }
    103. /**
    104. * 预览图片
    105. * @param fileName
    106. * @return
    107. */
    108. public String preview(String fileName,String bucketName){
    109. // 查看文件地址
    110. GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(bucketName).object(fileName).method(Method.GET).build();
    111. try {
    112. String url = minioClient.getPresignedObjectUrl(build);
    113. return url;
    114. } catch (Exception e) {
    115. e.printStackTrace();
    116. }
    117. return null;
    118. }
    119. /**
    120. * 文件下载
    121. * @param fileName 文件名称
    122. * @param res response
    123. * @return Boolean
    124. */
    125. public void download(String fileName,String bucketName, HttpServletResponse res) {
    126. GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
    127. .object(fileName).build();
    128. try (GetObjectResponse response = minioClient.getObject(objectArgs)){
    129. byte[] buf = new byte[1024];
    130. int len;
    131. try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()){
    132. while ((len=response.read(buf))!=-1){
    133. os.write(buf,0,len);
    134. }
    135. os.flush();
    136. byte[] bytes = os.toByteArray();
    137. res.setCharacterEncoding("utf-8");
    138. //设置强制下载不打开
    139. //res.setContentType("application/force-download");
    140. res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
    141. try (ServletOutputStream stream = res.getOutputStream()){
    142. stream.write(bytes);
    143. stream.flush();
    144. }
    145. }
    146. } catch (Exception e) {
    147. e.printStackTrace();
    148. }
    149. }
    150. /**
    151. * 查看文件对象
    152. * @return 存储bucket内文件对象信息
    153. */
    154. public List listObjects(String bucketName) {
    155. Iterable> results = minioClient.listObjects(
    156. ListObjectsArgs.builder().bucket(bucketName).build());
    157. List items = new ArrayList<>();
    158. try {
    159. for (Result result : results) {
    160. items.add(result.get());
    161. }
    162. } catch (Exception e) {
    163. e.printStackTrace();
    164. return null;
    165. }
    166. return items;
    167. }
    168. /**
    169. * 删除
    170. * @param fileName
    171. * @return
    172. * @throws Exception
    173. */
    174. public boolean remove(String fileName,String bucketName){
    175. try {
    176. minioClient.removeObject( RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
    177. }catch (Exception e){
    178. return false;
    179. }
    180. return true;
    181. }
    182. /**
    183. * 批量删除文件对象(没测试)
    184. * @param objects 对象名称集合
    185. */
    186. public Iterable> removeObjects(List objects, String bucketName) {
    187. List dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
    188. Iterable> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
    189. return results;
    190. }
    191. //上传文件,返回路径
    192. public Object upload(HashMap paramMap) {
    193. MultipartFile file=(MultipartFile)paramMap.get("file");
    194. if (file.isEmpty() || file.getSize() == 0) {
    195. return "文件为空";
    196. }
    197. try {
    198. if (!this.bucketExists("file")) {
    199. this.makeBucket("file");
    200. }
    201. InputStream inputStream = file.getInputStream();
    202. String fileName = file.getOriginalFilename();
    203. String newName = UUID.randomUUID().toString().replaceAll("-", "")
    204. + fileName.substring(fileName.lastIndexOf("."));
    205. this.upload("file", newName,file, inputStream);
    206. inputStream.close();
    207. String fileUrl = this.preview(newName,"file");
    208. String fileUrlNew=fileUrl.substring(0, fileUrl.indexOf("?"));
    209. JSONObject jsonObject=new JSONObject();
    210. jsonObject.put("url",fileUrlNew);
    211. return jsonObject.toJSONString();
    212. } catch (Exception e) {
    213. e.printStackTrace();
    214. return "上传失败";
    215. }
    216. }
    217. }

    项目中实践

    永久存储方式:把bucket的权限设置为public,返回路径到文件名称

  • 相关阅读:
    PAT 1021 Deepest Root(图的遍历,考察了连通块的数量,树的深度)
    vue3发送验证码倒计时 (防止连点、封装复用)
    【QT】使用toBase64方法将.txt文件的明文变为非明文(类似加密)
    SSM - Springboot - MyBatis-Plus 全栈体系(六)
    【Java中23种面试常考的设计模式之桥接模式(Bridge)---结构型模式】
    使用jconsole监控SpringbootJVM(JDK11)
    PR BeatEdit 节奏卡点神器 的报错 beat detection error: IBT failed 和解决路径
    非最小相位系统;频率特性的对称性
    【AI可视化---04】点亮数据之旅:发现Matplotlib的奇幻绘图世界!用Python挥洒数据音符的创意乐章——这四篇就够了!
    web前端-html自定义列表
  • 原文地址:https://blog.csdn.net/weixin_42383680/article/details/133552480