当前位置: 首页 > news >正文

Springboot集成minio实现文件上传与下载

文章目录

  • 前言
  • 一、前提准备
  • 二、代码
  • 三、启动测试


前言

上篇文章安装了minio,接下来就一块学习一下在springboot中集成minio实现文件上传下载。


一、前提准备

熟悉java语言编程,本地已经按照了idea开发工具,安装了java运行环境和maven。

二、代码

pom依赖jar包代码如下

    <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.3.7</version><exclusions><exclusion><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId></exclusion></exclusions></dependency><!-- 解决minio使用okhttp高版本的时候注入bean实例报错的问题 --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.9.3</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.78</version></dependency><dependency><groupId>com.mybatis-flex</groupId><artifactId>mybatis-flex-spring-boot-starter</artifactId><version>1.11.1</version></dependency><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency><!-- hutool 工具类  --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

application.yml 配置如下

# Minio 配置
minio:endpoint: http://127.0.0.1:9000accessKey: V8UDKIII9ynU6MP123 #Access key账户 写账号也可以secretKey: AG97879uLMpfYxOYfcTyfAq123111211 #Secret key密码bucketName: dev # 桶名称expire: 7200 # 过期时间

minio配置类如下

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;/*** minio配置属性*/
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {/*** Minio 连接地址*/private String endpoint;private int port;/*** accessKey 或 账号*/private String accessKey;/*** secretKey 或 密码*/private String secretKey;/*** 桶名称*/private String bucketName;/*** 默认是秒 地址过期时间,设置默认值7200秒*/private int expire = 7200;private boolean secure;@Beanpublic MinioClient minioClient() {try {return MinioClient.builder().endpoint(endpoint).credentials(accessKey,secretKey).build();} catch (Exception e) {throw new RuntimeException("Failed to initialize MinioClient!", e);}}
}

minio异常枚举类如下:

public enum ExceptionEnums {FILE_NAME_NOT_NULL("0001", "文件名不能为空"),BUCKET_NAME_NOT_NULL("0002", "桶名称不能为空"),FILE_NOT_EXIST("0003", "文件不存在"),BUCKET_NOT_EXIST("0004", "桶不存在"),BUCKET_NAME_NOT_EXIST("0005", "桶不存在,需要先创建桶在创建文件夹");//枚举类如果写方法的话,此处需要写分号private String code;private String msg;ExceptionEnums(String ecode, String emsg) {this.code = ecode;this.msg = emsg;}public String getCode() {return code;}public String getMsg() {return msg;}public static ExceptionEnums statOf(String code) {for (ExceptionEnums state : values())if (state.getCode().equals(code))return state;return null;}}

全局异常类如下:

public class GeneralException extends RuntimeException{private Integer errorCode;public GeneralException() {}public GeneralException(Throwable throwable) {super(throwable);}public GeneralException(String msg) {super(msg);this.errorCode = HttpStatus.INTERNAL_SERVER_ERROR.value();}public GeneralException(Integer errorCode, String msg) {super(msg);this.errorCode = errorCode;}public Integer getErrorCode() {return this.errorCode;}public void setErrorCode(Integer errorCode) {this.errorCode = errorCode;}}

minio工具类如下

import com.alibaba.fastjson.JSON;
import com.hymp.signalmodelservice.config.MinioConfig;
import com.hymp.signalmodelservice.enums.ExceptionEnums;
import com.hymp.signalmodelservice.exception.GeneralException;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.*;
/*** Minio 工具类*/
@Component
@Slf4j
public class MinioUtils {@Autowiredprivate MinioClient minioClient;@Autowiredprivate MinioConfig minioProperties;/*** 初始化Bucket*/private void createBucket(String bucketName) {try {// 判断 BucketName 是否存在if (bucketExists(bucketName)) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}} catch (Exception e) {e.printStackTrace();}}/*** 验证bucketName是否存在** @return boolean true:存在*/public boolean bucketExists(String bucketName) {if (StringUtils.isEmpty(bucketName)) {throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}boolean flag = true;try {flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());} catch (Exception e) {e.printStackTrace();}return flag;}/*** 获取全部bucket* <p>*/public List<String> getAllBuckets() {List<String> list = null;try {final List<Bucket> buckets = minioClient.listBuckets();list = new ArrayList<>(buckets.size());for (Bucket bucket : buckets) {list.add(bucket.name());}} catch (Exception e) {e.printStackTrace();}return list;}/*** 根据bucketName获取信息** @param bucketName bucket名称* @return*/public String getBucket(String bucketName) throws Exception {final Optional<Bucket> first = minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();String name = null;if (first.isPresent()) {name = first.get().name();}return name;}/*** 获取桶中文件名和大小列表** @param bucketName bucket名称* @param recursive  查询是否递归* @return*/public List<Object> getFileList(String bucketName, boolean recursive) {if (StringUtils.isEmpty(bucketName)) {throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}List<Object> items = new ArrayList<>();try {Iterable<Result<Item>> myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix("/2022-08-03/4674a894-abaf-48cb-9ea9-40a4e8560af9/Desktop").recursive(recursive).build());Iterator<Result<Item>> iterator = myObjects.iterator();String format = "{'fileName':'%s','fileSize':'%s'}";for (Result<Item> myObject : myObjects) {System.out.println(myObject.get().objectName());}while (iterator.hasNext()) {Item item = iterator.next().get();items.add(JSON.parse(String.format(format, item.objectName(), formatFileSize(item.size()))));
//                items.add(JSON.parse(String.format(format, "/".concat("test").concat("/").concat(item.objectName()), formatFileSize(item.size()))));}} catch (Exception e) {e.printStackTrace();log.info(e.getMessage());}items.remove(0);return items;}/*** 文件上传** @param file* @return*/public Map<String, Object> uploadFile(String bucketName, MultipartFile[] file) {if (file == null || file.length == 0) {throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}if (StringUtils.isEmpty(bucketName)) {throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}List<String> orgfileNameList = new ArrayList<>(file.length);for (MultipartFile multipartFile : file) {String orgfileName = multipartFile.getOriginalFilename();orgfileNameList.add(orgfileName);try {//文件上传InputStream in = multipartFile.getInputStream();minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(orgfileName).stream(in, multipartFile.getSize(), -1).contentType(multipartFile.getContentType()).build());in.close();} catch (Exception e) {e.printStackTrace();log.error(e.getMessage());}}Map<String, Object> data = new HashMap<>();data.put("bucketName", bucketName);data.put("fileName", orgfileNameList);return data;}/*** 获取上传文件的完整路径** @param bucketName 桶名称* @param fileName   文件名* @param expire     地址过期时间* @return*/public String getPresignedObjectUrl(String bucketName, String fileName, Integer expire) {if (StringUtils.isEmpty(bucketName)) {throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}if (StringUtils.isEmpty(fileName)) {throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}expire = Objects.isNull(expire) ? minioProperties.getExpire() : expire;// 验证桶是否存在在final boolean validationBucket = bucketExists(bucketName);if (!validationBucket) {throw new GeneralException(ExceptionEnums.BUCKET_NOT_EXIST.getMsg());}// 验证文件是否存在final boolean validationFileName = doFileNameExist(bucketName, fileName);if (!validationFileName) {throw new GeneralException(ExceptionEnums.FILE_NOT_EXIST.getMsg());}String url = null;try {// 获取桶和文件的完整路径url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileName).method(Method.GET).expiry(expire).build());} catch (MinioException e) {log.error("Error occurred: " + e);} catch (Exception e) {e.printStackTrace();}return url;}/*** 创建文件夹或目录** @param bucketName 存储桶* @param objectName 目录路径*/public Map<String, String> putDirObject(String bucketName, String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {// 判断桶是否存在if (!bucketExists(bucketName)) {throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_EXIST.getMsg());}final ObjectWriteResponse response = minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(new ByteArrayInputStream(new byte[]{}), 0, -1).build());Map<String, String> map = new HashMap<>();map.put("etag", response.etag());map.put("versionId", response.versionId());return map;}/*** 判断桶是否存在** @param bucketName 存储桶* @param objectName 文件夹名称(去掉/)* @return true:存在*/public boolean doFolderExist(String bucketName, String objectName) {boolean exist = false;try {Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());for (Result<Item> result : results) {Item item = result.get();if (item.isDir()) {exist = true;}}} catch (Exception e) {exist = false;}return exist;}/*** 判断文件是否存在** @param fileName 对象* @return true:存在*/public boolean doFileNameExist(String bucketName, String fileName) {if (StringUtils.isEmpty(bucketName)) {throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}if (StringUtils.isEmpty(fileName)) {throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}boolean exist = true;try {minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());} catch (Exception e) {exist = false;}return exist;}/*** 文件下载** @param response* @param fileName*/public void downloadFile(HttpServletResponse response, String bucketName, String fileName) {if (StringUtils.isEmpty(bucketName)) {throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}if (StringUtils.isEmpty(fileName)) {throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}// 判断文件是否存在final boolean flag = doFileNameExist(bucketName, fileName);if (!flag) {throw new GeneralException(ExceptionEnums.FILE_NOT_EXIST.getMsg());}InputStream in = null;try {// 获取对象信息StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());response.setContentType(stat.contentType());response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));//文件下载in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());IOUtils.copy(in, response.getOutputStream());} catch (Exception e) {log.error(e.getMessage());} finally {if (in != null) {try {in.close();} catch (IOException e) {log.error(e.getMessage());}}}}/*** 删除文件** @param bucketName bucket名称* @param fileName   文件名称*                   说明:当前方法不能真正删除,需要验证*/public void deleteFile(String bucketName, String fileName) {if (StringUtils.isEmpty(bucketName)) {throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}if (StringUtils.isEmpty(fileName)) {throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}try {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());} catch (Exception e) {log.error(e.getMessage());e.printStackTrace();}}/*** 批量文件删除** @param bucketName bucket名称* @param fileNames  文件名*/public void deleteBatchFile(String bucketName, List<String> fileNames) {if (StringUtils.isEmpty(bucketName)) {throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}if (CollectionUtils.isEmpty(fileNames)) {throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}try {List<DeleteObject> objects = new LinkedList<>();for (String fileName : fileNames) {objects.add(new DeleteObject(fileName));}Iterable<Result<DeleteError>> results =minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build());for (Result<DeleteError> result : results) {DeleteError error = result.get();log.error("Error occurred: " + error);}} catch (Exception e) {e.printStackTrace();log.error("批量删除失败!error:{}", e);}}/*** 文件大小** @param fileS* @return*/private static String formatFileSize(long fileS) {DecimalFormat df = new DecimalFormat("#.00");String fileSizeString = "";String wrongSize = "0B";if (fileS == 0) {return wrongSize;}if (fileS < 1024) {fileSizeString = df.format((double) fileS) + " B";} else if (fileS < 1048576) {fileSizeString = df.format((double) fileS / 1024) + " KB";} else if (fileS < 1073741824) {fileSizeString = df.format((double) fileS / 1048576) + " MB";} else {fileSizeString = df.format((double) fileS / 1073741824) + " GB";}return fileSizeString;}}

minio接口如下

@RestController
public class MinioController {@Autowiredprivate MinioUtils minioUtils;/*** 获取桶中文件名和大小列表** @return*/@GetMapping("/getFileList")public List<Object> getFileList() {return minioUtils.getFileList("dev", true);}/*** 判断文件是否存在** @param bucketName* @param fileName* @return*/@GetMapping("/doFileNameExist")public boolean doFileNameExist(String bucketName, String fileName) {return minioUtils.doFolderExist(bucketName, fileName);}/*** 上传文件** @param file* @return*/@PostMapping("/uploadFiles")public Map<String, Object> uploadFiles(String bucketName, @RequestParam(name = "file", required = false) MultipartFile[] file) {if (file == null || file.length == 0) {throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}if (StringUtils.isEmpty(bucketName)) {throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}return minioUtils.uploadFile(bucketName, file);}/*** 获取上传文件的完整浏览路径** @param filename* @return*/@GetMapping("/getPresignedObjectUrl")public String getPresignedObjectUrl(@RequestParam(name = "filename") String filename) {return minioUtils.getPresignedObjectUrl("dev", filename, null);}/*** 文件下载** @param response* @param fileName*/@GetMapping("/downloadFile")public void downloadFile(HttpServletResponse response, @RequestParam("fileName") String fileName) {minioUtils.downloadFile(response, "dev", fileName);}/*** 删除单个文件** @param fileName 完整路径(不包含bucket)*/@DeleteMapping("/deleteFile")public void deleteFile(String bucketName, String fileName) {minioUtils.deleteFile(bucketName, fileName);}/*** 批量删除文件** @param fileNames 完整路径(不包含bucket)*/@DeleteMapping("/deleteBatchFile")public void deleteBatchFile(String bucketName, @RequestParam("fileNames") List<String> fileNames) {minioUtils.deleteBatchFile(bucketName, fileNames);}}

三、启动测试

启动项目,打开postman 或 apifox 等http接口测试工具
输入地址:http://127.0.0.1:33333/uploadFiles
在这里插入图片描述

http://www.xdnf.cn/news/1478521.html

相关文章:

  • Go基础(②Viper)
  • 安装MATLAB205软件记录
  • 【Linux】Linux环境基础开发工具使用
  • clang(clangd)与arm-linux-gcc、ARMGCC、ICCARM(IAR)、C51编译器的兼容性
  • Nginx停止脚本命令
  • 性能优化的边界-不该优化什么
  • PostgreSQL三种关闭方式的区别
  • 详解文件操作
  • 数据库集群分类详解
  • 【Java】抽象类和接口对比+详解
  • Altium Designer(AD24)加载License文件方法
  • 计算机CPU的工作原理介绍
  • 抽成独立组件库:微前端架构下公共组件共享的最佳实践
  • MyBatis Example模式SQL注入风险
  • C#中一段程序类比博图
  • 【完整源码+数据集+部署教程】水培植物病害检测系统源码和数据集:改进yolo11-AKConv
  • 从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
  • 【科研成果速递-IJGIS】如何描述与分类移动对象的时空模式?一个新的分类框架与体系!
  • JDBC操作数据库所需要的组件
  • 《Kubernetes 构建 MySQL MGR 集群实战教程》
  • 使用Spring Boot DevTools快速重启功能
  • Python爬虫实战:研究Event Handling机制,构建在线教育平台的课程数据采集和分析系统
  • 使用 YAML 自动化 Azure DevOps 管道
  • browser-use 的三种启动方式详解
  • Android Framework智能座舱面试题
  • 【Python自动化】 21.2 Pandas 读取 Excel 时的 dtype 参数完全指南
  • 贪心算法应用:DNA自组装问题详解
  • Flask论坛与个人中心页面开发教程完整详细版
  • 【LeetCode 热题 100】49. 字母异位词分组
  • Windows 11 手动下载安装配置 uv、配置国内源