当前位置:   article > 正文

Springboot整合Minio实现文件上传、下载_minio文件下载

minio文件下载

目录

前言

一、环境准备

二、环境配置

1、引入依赖

2、添加配置文件

三、代码编写

1、添加配置类

2、添加工具类

四、方法测试

1、文件上传

注意:

2、文件下载

结语


前言

springboot作为目前主流的java开发框架,用来简化spring程序的创建和开发过程。在实际开发过程中我们往往会遇到部分涉及文件上传、下载的场景。经过多方筛选最终选用了Minio作为项目与中的文件存储工具。

Minio作为一款高性能、可扩展、分布式对象存储系统,并且它是完全开源免费的一款工具,非常适合实际项目的使用!!!

关于Minio的Windows的安装教程,各位小伙伴可以参考上一篇Minio安装的教程

地址:Minio基于Window系统的安装与使用-CSDN博客

下面让我们一起来了解一下SpringBoot整合Minio的具体实现方式。

一、环境准备

在使用Minio之前我们要做的第一件事情就是有一个SpringBoot项目,需要小伙伴自行创建或者使用现有项目。土豆这里使用的是自己原有的一个若以框架改过来的一个项目,但是这个项目此前没有使用过Minio,确认这点有助于更加顺利的完成我们的测试。

如果小伙伴的项目中原本使用过Minio的,也没关系哈,可以根据本文做一个参考进一步完善项目内容。

准备好项目之后咱们就可以开始本次的任务了,请跟随土豆一起搞起来,呦吼!!!

二、环境配置

1、引入依赖

在pom.xml中引入Minio的依赖:

  1. <dependencies>
  2. <!--Minio-->
  3. <dependency>
  4. <groupId>io.minio</groupId>
  5. <artifactId>minio</artifactId>
  6. <version>8.4.6</version>
  7. </dependency>
  8. <!--okhttp3 依赖-->
  9. <dependency>
  10. <groupId>com.squareup.okhttp3</groupId>
  11. <artifactId>okhttp</artifactId>
  12. <version>4.9.3</version>
  13. </dependency>
  14. </dependencies>

2、添加配置文件

application.yml配置信息

  1. # 配置minio
  2. minio:
  3. server: http://localhost
  4. port: 9000
  5. endpoint: ${minio.server}:${minio.port}
  6. access-key: admin # minio账号
  7. secret-key: admin118110 # minio密码
  8. bucket: demo001 # 指定桶名

三、代码编写

完成项目的基础配置之后,接下来咱开始来编写我们的代码

1、添加配置类

  1. import io.minio.MinioClient;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.stereotype.Component;
  6. /**
  7. * @author YHL
  8. * @date 2024/4/24 9:36
  9. * @description 当前配置类不建议使用@Data注解,需要手动生成get,set方法
  10. */
  11. @Component
  12. @ConfigurationProperties(prefix = "minio")
  13. public class MinIoClientConfig {
  14. @Value("${minio.server}")
  15. private String server;
  16. @Value("${minio.port}")
  17. private int port;
  18. @Value("${minio.endpoint}")
  19. private String endpoint;
  20. @Value("${minio.access-key}")
  21. private String accessKey;
  22. @Value("${minio.secret-key}")
  23. private String secretKey;
  24. @Value("${minio.bucket}")
  25. private String bucket;
  26. /**
  27. * 创建minio连接对象
  28. * @return
  29. */
  30. @Bean
  31. public MinioClient minioClient(){
  32. return MinioClient.builder()
  33. .endpoint(server,port,false)
  34. .credentials(accessKey,secretKey)
  35. .build();
  36. }
  37. public String getServer() {
  38. return server;
  39. }
  40. public void setServer(String server) {
  41. this.server = server;
  42. }
  43. public int getPort() {
  44. return port;
  45. }
  46. public void setPort(int port) {
  47. this.port = port;
  48. }
  49. public String getEndpoint() {
  50. return endpoint;
  51. }
  52. public void setEndpoint(String endpoint) {
  53. this.endpoint = endpoint;
  54. }
  55. public String getAccessKey() {
  56. return accessKey;
  57. }
  58. public void setAccessKey(String accessKey) {
  59. this.accessKey = accessKey;
  60. }
  61. public String getSecretKey() {
  62. return secretKey;
  63. }
  64. public void setSecretKey(String secretKey) {
  65. this.secretKey = secretKey;
  66. }
  67. public String getBucket() {
  68. return bucket;
  69. }
  70. public void setBucket(String bucket) {
  71. this.bucket = bucket;
  72. }
  73. }

2、添加工具类

  1. package com.psy.common.utils;
  2. import cn.hutool.core.util.ObjectUtil;
  3. import io.minio.*;
  4. import io.minio.http.Method;
  5. import io.minio.messages.Bucket;
  6. import io.minio.messages.DeleteError;
  7. import io.minio.messages.DeleteObject;
  8. import io.minio.messages.Item;
  9. import lombok.SneakyThrows;
  10. import org.apache.commons.lang.BooleanUtils;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.beans.factory.annotation.Value;
  13. import org.springframework.stereotype.Component;
  14. import org.springframework.web.multipart.MultipartFile;
  15. import javax.servlet.http.HttpServletResponse;
  16. import java.io.*;
  17. import java.net.URLEncoder;
  18. import java.nio.charset.StandardCharsets;
  19. import java.text.SimpleDateFormat;
  20. import java.util.*;
  21. import java.util.concurrent.TimeUnit;
  22. import java.util.stream.Collectors;
  23. /**
  24. * @author YHL
  25. * @date 2024/4/24 9:51
  26. * @description
  27. */
  28. @Component
  29. public class MinioUtil {
  30. @Autowired
  31. private MinioClient minioClient;
  32. @Value("${minio.bucket}")
  33. private String bucket;
  34. /**
  35. * description: 判断bucket是否存在,不存在则创建
  36. */
  37. @SneakyThrows
  38. public boolean existBucket(String name) {
  39. boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
  40. if (!exists) {
  41. minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
  42. }
  43. return exists;
  44. }
  45. /**
  46. * 创建存储bucket
  47. *
  48. * @param bucketName 存储bucket名称
  49. * @return Boolean
  50. */
  51. @SneakyThrows
  52. public Boolean makeBucket(String bucketName) {
  53. boolean exist = existBucket(bucketName);
  54. if (!exist) {
  55. minioClient.makeBucket(MakeBucketArgs.builder()
  56. .bucket(bucketName)
  57. .build());
  58. return true;
  59. }
  60. return false;
  61. }
  62. /**
  63. * 删除存储bucket
  64. *
  65. * @param bucketName 存储bucket名称
  66. * @return Boolean
  67. */
  68. @SneakyThrows
  69. public Boolean removeBucket(String bucketName) {
  70. boolean exist = existBucket(bucketName);
  71. if (!exist) return false;
  72. Iterable<Result<Item>> results =
  73. minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
  74. for (Result<Item> result : results) {
  75. Item item = result.get();
  76. // 桶不为空不允许删除
  77. if (item.size() > 0) {
  78. return false;
  79. }
  80. }
  81. minioClient.removeBucket(RemoveBucketArgs.builder()
  82. .bucket(bucketName)
  83. .build());
  84. return true;
  85. }
  86. /**
  87. * 列出所有存储桶
  88. */
  89. @SneakyThrows
  90. public List<Bucket> listBuckets() {
  91. return minioClient.listBuckets();
  92. }
  93. /**
  94. * 列出所有存储桶名称
  95. */
  96. public List<String> listBucketNames() {
  97. List<Bucket> bucketList = listBuckets();
  98. if (ObjectUtil.isEmpty(bucketList))
  99. return null;
  100. List<String> bucketListName = new ArrayList<>();
  101. for (Bucket bucket : bucketList) {
  102. bucketListName.add(bucket.name());
  103. }
  104. return bucketListName;
  105. }
  106. /**
  107. * 列出存储桶中的所有对象名称
  108. *
  109. * @param bucketName 存储桶名称
  110. */
  111. @SneakyThrows
  112. public List<String> listObjectNames(String bucketName) {
  113. boolean exist = existBucket(bucketName);
  114. if (!exist) return null;
  115. List<String> listObjectNames = new ArrayList<>();
  116. Iterable<Result<Item>> results =
  117. minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
  118. for (Result<Item> result : results) {
  119. Item item = result.get();
  120. listObjectNames.add(item.objectName());
  121. }
  122. return listObjectNames;
  123. }
  124. /**
  125. * 查看文件对象
  126. *
  127. * @param bucketName 存储bucket名称
  128. * @return 存储bucket内文件对象信息
  129. */
  130. public Map<String, Object> listObjects(String bucketName) {
  131. boolean exist = existBucket(bucketName);
  132. if (!exist) return null;
  133. Iterable<Result<Item>> results =
  134. minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
  135. Map<String, Object> map = new HashMap<>();
  136. try {
  137. for (Result<Item> result : results) {
  138. Item item = result.get();
  139. map.put(item.objectName(), item);
  140. }
  141. } catch (Exception e) {
  142. e.printStackTrace();
  143. return null;
  144. }
  145. return map;
  146. }
  147. /**
  148. * 文件访问路径
  149. *
  150. * @param bucketName 存储桶名称
  151. * @param objectName 存储桶里的对象名称
  152. */
  153. @SneakyThrows
  154. public String getObjectUrl(String bucketName, String objectName) {
  155. boolean exist = existBucket(bucketName);
  156. if (!exist) return null;
  157. return minioClient.getPresignedObjectUrl(
  158. GetPresignedObjectUrlArgs.builder()
  159. .method(Method.GET)
  160. .bucket(bucketName)
  161. .object(objectName)
  162. .expiry(2, TimeUnit.MINUTES)
  163. .build());
  164. }
  165. /**
  166. * 删除一个对象
  167. *
  168. * @param bucketName 存储桶名称
  169. * @param objectName 存储桶里的对象名称
  170. */
  171. @SneakyThrows
  172. public boolean removeObject(String bucketName, String objectName) {
  173. boolean exist = existBucket(bucketName);
  174. if (!exist) return false;
  175. minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
  176. return true;
  177. }
  178. /**
  179. * 删除指定桶的多个文件对象
  180. *
  181. * @param bucketName 存储桶名称
  182. * @param objectNames 含有要删除的多个object名称的迭代器对象
  183. */
  184. @SneakyThrows
  185. public boolean removeObject(String bucketName, List<String> objectNames) {
  186. boolean exist = existBucket(bucketName);
  187. if (!exist) return false;
  188. List<DeleteObject> objects = new LinkedList<>();
  189. for (String objectName : objectNames) {
  190. objects.add(new DeleteObject(objectName));
  191. }
  192. minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build());
  193. return true;
  194. }
  195. /**
  196. * 批量删除文件对象
  197. *
  198. * @param bucketName 存储bucket名称
  199. * @param objects 对象名称集合
  200. */
  201. public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
  202. List<DeleteObject> dos = objects.stream().map(DeleteObject::new).collect(Collectors.toList());
  203. return minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
  204. }
  205. /**
  206. * 文件上传
  207. */
  208. public String upload(MultipartFile multipartFile) {
  209. String fileName = multipartFile.getOriginalFilename();
  210. // 注意,这里需要加上 \\ 将 特殊字符 . 转意 \\. ,否则异常
  211. assert fileName != null;
  212. String[] fileArray = fileName.split("\\.");
  213. // 获取当前日期
  214. Date now = new Date();
  215. SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
  216. String format = sdf.format(now);
  217. fileName = format + "/" + fileArray[0] + System.currentTimeMillis() + "." + fileArray[1];
  218. try {
  219. InputStream inputStream = multipartFile.getInputStream();
  220. // 上传到minio服务器
  221. minioClient.putObject(PutObjectArgs.builder()
  222. .bucket(bucket)
  223. .object(fileName)
  224. .stream(inputStream, -1L, 10485760L)
  225. .build());
  226. } catch (Exception e) {
  227. e.printStackTrace();
  228. }
  229. // 返回地址
  230. return fileName;
  231. }
  232. /**
  233. * 文件下载
  234. *
  235. * @param fileName 文件名
  236. * @param delete 是否删除
  237. */
  238. public void fileDownload(String fileName, Boolean delete, HttpServletResponse response) {
  239. InputStream inputStream = null;
  240. OutputStream outputStream = null;
  241. try {
  242. if (StringUtils.isBlank(fileName)) {
  243. response.setHeader("Content-type", "text/html;charset=UTF-8");
  244. String data = "文件下载失败";
  245. OutputStream ps = response.getOutputStream();
  246. ps.write(data.getBytes(StandardCharsets.UTF_8));
  247. return;
  248. }
  249. outputStream = response.getOutputStream();
  250. // 获取文件对象
  251. inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(fileName).build());
  252. byte[] buf = new byte[1024];
  253. int length = 0;
  254. response.reset();
  255. response.setHeader("Content-Disposition", "attachment;filename=" +
  256. URLEncoder.encode(fileName.substring(fileName.lastIndexOf("/") + 1), "UTF-8"));
  257. response.setContentType("application/octet-stream");
  258. response.setCharacterEncoding("UTF-8");
  259. // 输出文件
  260. while ((length = inputStream.read(buf)) > 0) {
  261. outputStream.write(buf, 0, length);
  262. }
  263. inputStream.close();
  264. // 判断:下载后是否同时删除minio上的存储文件
  265. if (BooleanUtils.isTrue(delete)) {
  266. minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(fileName).build());
  267. }
  268. } catch (Throwable ex) {
  269. response.setHeader("Content-type", "text/html;charset=UTF-8");
  270. String data = "文件下载失败";
  271. try {
  272. OutputStream ps = response.getOutputStream();
  273. ps.write(data.getBytes(StandardCharsets.UTF_8));
  274. } catch (IOException e) {
  275. e.printStackTrace();
  276. }
  277. } finally {
  278. try {
  279. outputStream.close();
  280. if (inputStream != null) {
  281. inputStream.close();
  282. }
  283. } catch (IOException e) {
  284. e.printStackTrace();
  285. }
  286. }
  287. }
  288. }

在util中我们已经针对Minio的使用定义好了各种常用方法,如果有遗漏欢迎小伙伴们私信我做出补充。

四、方法测试

代码编写完成之后我们就开始测试我们代码的可用性,在这土豆简单编写了一个controller,主要用来测试我们常用的文件上传,文件下载方法。

代码如下:

  1. import com.psy.common.core.controller.BaseController;
  2. import com.psy.common.core.domain.AjaxResult;
  3. import com.psy.common.utils.MinioUtil;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import org.springframework.web.multipart.MultipartFile;
  7. import javax.annotation.Resource;
  8. import javax.servlet.http.HttpServletResponse;
  9. /**
  10. * @author YHL
  11. * @date 2024/4/24 8:54
  12. * @description
  13. */
  14. @RestController
  15. @RequestMapping("/fileDemo")
  16. public class FileDemoController extends BaseController {
  17. @Resource
  18. private MinioUtil minioUtil;
  19. @RequestMapping("/upload")
  20. public AjaxResult upload(MultipartFile multipartFile) {
  21. return AjaxResult.success(minioUtil.upload(multipartFile));
  22. }
  23. @RequestMapping("/download")
  24. public void download(String fileName, HttpServletResponse response) {
  25. minioUtil.fileDownload(fileName, false, response);
  26. }
  27. }

然后我们可以使用测试工具对我们的代码进行测试,查看功能是否实现。

土豆这里使用的测试工具是ApiFox,我个人觉得是这款测试工具比传统的PostMan要美观要好用,有想去的小伙伴可以尝试下载使用一下。

好了,废话不多说,咱开测。

1、文件上传

首先查看我们的Minio指定的桶的状态是否正常,可以看到咱的桶中是没有文件的:

然后我们在ApiFox中输入接口地址以及选择要上传的文件

点击发送之后,返回值显示文件上传完成

然后我们再到Minio中查看

发现所上传的图片已经上传到Minio中指定的桶中了,并且文件的上级地址是当前的日期。

所以说我们的代码是没有问题的,在实际使用中,我们肯定会有将文件地址存储起来的操作

在这里我要提醒大家,

注意:

1)为了方便以后的维护或者是数据迁移等操作,存储时只需要存储文件的相对路径,如果有需要存储桶名或者其他相关信息的话,建议区分字段存储,以避免不必要的麻烦。

2)这里文件上传使用的文件名称采用了原文件名称,但是在实际场景中,这种操作并不可取,为了避免文件冲突以及方便区分文件,建议在文件上传时在数据库中保存文件原名称,并重命名上传文件的名称(可采用时间戳等方式确保文件名称唯一)

2、文件下载

上面完成了文件上传操作后Minio中已经存在一个文件了,现在我们需要将这个文件下载下来,我们继续使用ApiFox来测试:

点击发送:

到这里之后我们的文件下载和文件上传都是成功的。

结语

以上内容就是结合上篇Minio基于Window系统的安装与使用的分享整合的SpringBoot整合Minio的内容

最后还是希望本次分享对您能够有所帮助,如有不足还请各位多多指点,多多支持。

感激不尽,感激不尽。。。。。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小惠珠哦/article/detail/873354
推荐阅读
相关标签
  

闽ICP备14008679号