一、开发前戏

1.先导入maven依赖

( MinIO版本不能太低不然运行时会出现:org.xmlpull.v1.XmlPullParserException 错误)

  1. <!-- minio 相关依赖 -->
  2. <dependency>
  3. <groupId>io.minio</groupId>
  4. <artifactId>minio</artifactId>
  5. <version>3.0.10</version>
  6. </dependency>
  7. <!-- alibaba的fastjson -->
  8. <dependency>
  9. <groupId>com.alibaba</groupId>
  10. <artifactId>fastjson</artifactId>
  11. <version>1.2.51</version>
  12. </dependency>
  13. <!-- thymeleaf模板引擎 -->
  14. <dependency>
  15. <groupId>org.springframework.boot</groupId>
  16. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  17. </dependency>

2、添加配置信息

在 application.yml 文件中加入 MinIO 服务器的相关信息

  1. #minio 文件存储配置信息
  2. minio:
  3. endpoint: http://127.0.0.1:9000
  4. accesskey: admin
  5. secretKey: admin123456

3、创建实体类package com.zyxx.email.common.minio;

  1. import lombok.Data;
  2. import org.springframework.boot.context.properties.ConfigurationProperties;
  3. import org.springframework.stereotype.Component;
  4. /**
  5. * minio 属性值
  6. */
  7. @Data
  8. @Component
  9. @ConfigurationProperties(prefix = "minio")
  10. public class MinioProp {
  11. /**
  12. * 连接url
  13. */
  14. private String endpoint;
  15. /**
  16. * 用户名
  17. */
  18. private String accesskey;
  19. /**
  20. * 密码
  21. */
  22. private String secretKey;
  23. }

这一步,我们将配置文件中 minio 的配置信息通过注解的方式注入到 MinioProp 这个实体中,方便后面我们使用

4、创建核心配置类

  1. package com.zyxx.email.common.minio;
  2. import io.minio.MinioClient;
  3. import io.minio.errors.InvalidEndpointException;
  4. import io.minio.errors.InvalidPortException;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.boot.context.properties.EnableConfigurationProperties;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. /**
  10. * minio 核心配置类
  11. */
  12. @Configuration
  13. @EnableConfigurationProperties(MinioProp.class)
  14. public class MinioConfig {
  15. @Autowired
  16. private MinioProp minioProp;
  17. /**
  18. * 获取 MinioClient
  19. *
  20. * @return
  21. * @throws InvalidPortException
  22. * @throws InvalidEndpointException
  23. */
  24. @Bean
  25. public MinioClient minioClient() throws InvalidPortException, InvalidEndpointException {
  26. return new MinioClient(minioProp.getEndpoint(), minioProp.getAccesskey(), minioProp.getSecretKey());
  27. }
  28. }

通过注入 MinIO 服务器的相关配置信息,得到 MinioClient 对象,我们上传文件依赖此对象

5、上传工具类

  1. package com.zyxx.email.common.minio;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.zyxx.email.common.redis.RedisUtil;
  4. import com.zyxx.email.utils.DateUtils;
  5. import io.minio.MinioClient;
  6. import lombok.SneakyThrows;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Component;
  10. import org.springframework.web.multipart.MultipartFile;
  11. @Slf4j
  12. @Component
  13. public class MinioUtils {
  14. @Autowired
  15. private MinioClient client;
  16. @Autowired
  17. private MinioProp minioProp;
  18. /**
  19. * 创建bucket
  20. *
  21. * @param bucketName bucket名称
  22. */
  23. @SneakyThrows
  24. public void createBucket(String bucketName) {
  25. if (!client.bucketExists(bucketName)) {
  26. client.makeBucket(bucketName);
  27. }
  28. }
  29. /**
  30. * 上传文件
  31. *
  32. * @param file 文件
  33. * @param bucketName 存储桶
  34. * @return
  35. */
  36. public JSONObject uploadFile(MultipartFile file, String bucketName) throws Exception {
  37. JSONObject res = new JSONObject();
  38. res.put("code", 0);
  39. // 判断上传文件是否为空
  40. if (null == file || 0 == file.getSize()) {
  41. res.put("msg", "上传文件不能为空");
  42. return res;
  43. }
  44. // 判断存储桶是否存在
  45. createBucket(bucketName);
  46. // 文件名
  47. String originalFilename = file.getOriginalFilename();
  48. // 新的文件名 = 存储桶名称_时间戳.后缀名
  49. String fileName = bucketName + "_" + System.currentTimeMillis() + originalFilename.substring(originalFilename.lastIndexOf("."));
  50. // 开始上传
  51. client.putObject(bucketName, fileName, file.getInputStream(), file.getContentType());
  52. res.put("code", 1);
  53. res.put("msg", minioProp.getEndpoint() + "/" + bucketName + "/" + fileName);
  54. return res;
  55. }
  56. }

二、开发进行中

1、编写 Controller

  1. package com.zyxx.email.controller;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.zyxx.email.common.minio.MinioUtils;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Controller;
  6. import org.springframework.web.bind.annotation.*;
  7. import org.springframework.web.multipart.MultipartFile;
  8. import javax.servlet.http.HttpServletRequest;
  9. @Controller
  10. public class MinioController {
  11. @Autowired
  12. private MinioUtils minioUtils;
  13. @GetMapping("init")
  14. public String init() {
  15. return "file";
  16. }
  17. /**
  18. * 上传
  19. *
  20. * @param file
  21. * @param request
  22. * @return
  23. */
  24. @PostMapping("/upload")
  25. @ResponseBody
  26. public String upload(@RequestParam(name = "file", required = false) MultipartFile file, HttpServletRequest request) {
  27. JSONObject res = null;
  28. try {
  29. res = minioUtils.uploadFile(file, "product");
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. res.put("code", 0);
  33. res.put("msg", "上传失败");
  34. }
  35. return res.toJSONString();
  36. }
  37. }

2、上传页面

  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>首页</title>
  6. </head>
  7. <body>
  8. <form accept-charset="UTF-8" th:action="@{upload}" method="post" enctype="multipart/form-data" target="_blank">
  9. 文件:<input type="file" name="file"/>
  10. <input type="submit" value="上传"/>
  11. </form>
  12. </body>
  13. </html>

这里我用的 thymeleaf 模板引擎

三、上传测试

1、访问地址

http://localhost:8080/init

image.png

2、启动 MinIO 文件服务器

3、响应信息

{“msg”:”http://127.0.0.1:9000/product/product_1589105654237.png”,”code”:1}
1
http://127.0.0.1:9000/product/product_1589105654237.png 就是我们上传之后得到的文件地址了

4、访问文件

MinIO 形式上传的文件也不支持直接访问,我们如果需要直接访问,还需要做如下操作:
image.png
设置 bucket 的 policy 策略:
image.png
设置该存储桶下面的文件为 Read and Write,这时我们就可以直接访问了
image.png

四、完整工具类代码

  1. package com.zyxx.email.common.minio;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.zyxx.email.utils.DateUtils;
  4. import io.minio.MinioClient;
  5. import io.minio.ObjectStat;
  6. import io.minio.messages.Bucket;
  7. import lombok.SneakyThrows;
  8. import lombok.extern.slf4j.Slf4j;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.stereotype.Component;
  11. import org.springframework.web.multipart.MultipartFile;
  12. import java.io.InputStream;
  13. import java.util.*;
  14. @Slf4j
  15. @Component
  16. public class MinioUtils {
  17. @Autowired
  18. private MinioClient client;
  19. @Autowired
  20. private MinioProp minioProp;
  21. /**
  22. * 创建bucket
  23. *
  24. * @param bucketName bucket名称
  25. */
  26. @SneakyThrows
  27. public void createBucket(String bucketName) {
  28. if (!client.bucketExists(bucketName)) {
  29. client.makeBucket(bucketName);
  30. }
  31. }
  32. /**
  33. * 获取全部bucket
  34. */
  35. @SneakyThrows
  36. public List<Bucket> getAllBuckets() {
  37. return client.listBuckets();
  38. }
  39. /**
  40. * 根据bucketName获取信息
  41. *
  42. * @param bucketName bucket名称
  43. */
  44. @SneakyThrows
  45. public Optional<Bucket> getBucket(String bucketName) {
  46. return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
  47. }
  48. /**
  49. * 根据bucketName删除信息
  50. *
  51. * @param bucketName bucket名称
  52. */
  53. @SneakyThrows
  54. public void removeBucket(String bucketName) {
  55. client.removeBucket(bucketName);
  56. }
  57. /**
  58. * 获取文件外链
  59. *
  60. * @param bucketName bucket名称
  61. * @param objectName 文件名称
  62. * @param expires 过期时间 <=7
  63. * @return url
  64. */
  65. @SneakyThrows
  66. public String getObjectURL(String bucketName, String objectName, Integer expires) {
  67. return client.presignedGetObject(bucketName, objectName, expires);
  68. }
  69. /**
  70. * 获取文件
  71. *
  72. * @param bucketName bucket名称
  73. * @param objectName 文件名称
  74. * @return 二进制流
  75. */
  76. @SneakyThrows
  77. public InputStream getObject(String bucketName, String objectName) {
  78. return client.getObject(bucketName, objectName);
  79. }
  80. /**
  81. * 上传文件
  82. *
  83. * @param bucketName bucket名称
  84. * @param objectName 文件名称
  85. * @param stream 文件流
  86. * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
  87. */
  88. public void putObject(String bucketName, String objectName, InputStream stream) throws Exception {
  89. client.putObject(bucketName, objectName, stream, stream.available(), "application/octet-stream");
  90. }
  91. /**
  92. * 上传文件
  93. *
  94. * @param bucketName bucket名称
  95. * @param objectName 文件名称
  96. * @param stream 文件流
  97. * @param size 大小
  98. * @param contextType 类型
  99. * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
  100. */
  101. public void putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {
  102. client.putObject(bucketName, objectName, stream, size, contextType);
  103. }
  104. /**
  105. * 获取文件信息
  106. *
  107. * @param bucketName bucket名称
  108. * @param objectName 文件名称
  109. * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
  110. */
  111. public ObjectStat getObjectInfo(String bucketName, String objectName) throws Exception {
  112. return client.statObject(bucketName, objectName);
  113. }
  114. /**
  115. * 删除文件
  116. *
  117. * @param bucketName bucket名称
  118. * @param objectName 文件名称
  119. * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject
  120. */
  121. public void removeObject(String bucketName, String objectName) throws Exception {
  122. client.removeObject(bucketName, objectName);
  123. }
  124. /**
  125. * 上传文件
  126. *
  127. * @param file 文件
  128. * @param bucketName 存储桶
  129. * @return
  130. */
  131. public JSONObject uploadFile(MultipartFile file, String bucketName) throws Exception {
  132. JSONObject res = new JSONObject();
  133. res.put("code", 0);
  134. // 判断上传文件是否为空
  135. if (null == file || 0 == file.getSize()) {
  136. res.put("msg", "上传文件不能为空");
  137. return res;
  138. }
  139. // 判断存储桶是否存在
  140. createBucket(bucketName);
  141. // 文件名
  142. String originalFilename = file.getOriginalFilename();
  143. // 新的文件名
  144. String fileName = bucketName + "_" + DateUtils.getYyyymmdd() + originalFilename.substring(originalFilename.lastIndexOf("."));
  145. // 开始上传
  146. client.putObject(bucketName, fileName, file.getInputStream(), file.getContentType());
  147. res.put("code", 1);
  148. res.put("msg", minioProp.getEndpoint() + "/" + bucketName + "/" + fileName);
  149. return res;
  150. }
  151. }

文件的下载功能代码,在后续的文章中会贴出

Java Client 指南地址如下:
https://docs.min.io/docs/java-client-quickstart-guide.html