0


SpringBoot整合Minio的详细步骤

小伙伴们好,欢迎关注,一起学习,无限进步

minio 是对象存储服务。它基于 Apache License 开源协议,兼容 Amazon S3 云存储接口。适合存储非结构化数据,如图片,音频,视频,日志等。对象文件最大可以达到 5TB。

优点有高性能,可扩展,操作简单,有图形化操作界面,读写性能优异等。官网

minio 部署可参考这篇:Minio 详细安装部署步骤

SpringBoot 快速整合 minio

1、添加 Maven 依赖

pom.xml

文件中添加MinIO客户端依赖项

<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version></dependency><!-- 如果 minio 版本是8.3.4以上 okhttp 一定要大于 4.8.1 版本 --><!--        <dependency>--><!--            <groupId>com.squareup.okhttp3</groupId>--><!--            <artifactId>okhttp</artifactId>--><!--            <version>4.8.2</version>--><!--        </dependency>--><!-- 还用到了 fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.78</version></dependency>

2、配置MinIO连接信息

application.properties

application.yml

文件中配置MinIO的连接信息,包括服务器地址、端口、凭据等信息

# Minio 配置
minio.endpoint=127.0.0.1:9000 #对象存储服务的URL
minio.accessKey=admin #Access key账户 写账号也可以
minio.secretKey=admin #Secret key密码
minio.bucketName=test # 桶名称
# 过期时间
minio.expire=7200
# Minio 配置minio:endpoint:  127.0.0.1:9000#对象存储服务的URLaccessKey: admin #Access key账户 写账号也可以secretKey: admin #Secret key密码bucketName: test # 桶名称expire:7200# 过期时间

3、创建 MinIO 客户端 Bean

在 SpringBoot 应用的配置类中创建一个 MinIO客户端的 Bean,以便在应用中使用 MinIO 服务

importlombok.Data;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.stereotype.Component;/**
 * minio 配置属性
 */@Data@Component@ConfigurationProperties(prefix ="minio")publicclassMinioProperties{/**
     * Minio 连接地址
     */privateString endpoint;/**
     * accessKey 或 账号
     */privateString accessKey;/**
     * secretKey 或 密码
     */privateString secretKey;/**
     * 桶名称
     */privateString bucketName;/**
     * 默认是秒 地址过期时间,设置默认值7200秒
     */privateint expire =7200;}

4、异常枚举类

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

5、全局异常

importorg.springframework.http.HttpStatus;/**
 * 异常
 */publicclassGeneralExceptionextendsRuntimeException{privateInteger errorCode;publicGeneralException(){}publicGeneralException(Throwable throwable){super(throwable);}publicGeneralException(String msg){super(msg);this.errorCode =HttpStatus.INTERNAL_SERVER_ERROR.value();}publicGeneralException(Integer errorCode,String msg){super(msg);this.errorCode = errorCode;}publicIntegergetErrorCode(){returnthis.errorCode;}publicvoidsetErrorCode(Integer errorCode){this.errorCode = errorCode;}}

6、minio 工具类

工具类包含创建 bucket,获取全部 bucket,获取 bucket 文件名和大小列表,文件上传,获取上传文件的完整路径,创建文件夹或目录,判断 bucket 是否存在,判断文件是否存在,文件下载,删除文件,批量删除文件方法

importcom.alibaba.fastjson.JSON;importio.minio.*;importio.minio.errors.*;importio.minio.http.Method;importio.minio.messages.Bucket;importio.minio.messages.DeleteError;importio.minio.messages.DeleteObject;importio.minio.messages.Item;importlombok.extern.slf4j.Slf4j;importorg.apache.tomcat.util.http.fileupload.IOUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importorg.springframework.util.CollectionUtils;importorg.springframework.util.StringUtils;importorg.springframework.web.multipart.MultipartFile;importjavax.servlet.http.HttpServletResponse;importjava.io.ByteArrayInputStream;importjava.io.IOException;importjava.io.InputStream;importjava.net.URLEncoder;importjava.security.InvalidKeyException;importjava.security.NoSuchAlgorithmException;importjava.text.DecimalFormat;importjava.util.*;/**
 * Minio 工具类
 */@Component@Slf4jpublicclassMinioUtils{@AutowiredprivateMinioClient minioClient;@AutowiredprivateMinioProperties minioProperties;/**
     * 初始化Bucket
     */privatevoidcreateBucket(String bucketName){try{// 判断 BucketName 是否存在if(bucketExists(bucketName)){
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}catch(Exception e){
            e.printStackTrace();}}/**
     * 验证bucketName是否存在
     *
     * @return boolean true:存在
     */publicbooleanbucketExists(String bucketName){if(StringUtils.isEmpty(bucketName)){thrownewGeneralException(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>
     */publicList<String>getAllBuckets(){List<String> list =null;try{finalList<Bucket> buckets = minioClient.listBuckets();
            list =newArrayList<>(buckets.size());for(Bucket bucket : buckets){
                list.add(bucket.name());}}catch(Exception e){
            e.printStackTrace();}return list;}/**
     * 根据bucketName获取信息
     *
     * @param bucketName bucket名称
     * @return
     */publicStringgetBucket(String bucketName)throwsException{finalOptional<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
     */publicList<Object>getFileList(String bucketName,boolean recursive){if(StringUtils.isEmpty(bucketName)){thrownewGeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}List<Object> items =newArrayList<>();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
     */publicMap<String,Object>uploadFile(String bucketName,MultipartFile[] file){if(file ==null|| file.length ==0){thrownewGeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}if(StringUtils.isEmpty(bucketName)){thrownewGeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}List<String> orgfileNameList =newArrayList<>(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 =newHashMap<>();
        data.put("bucketName", bucketName);
        data.put("fileName", orgfileNameList);return data;}/**
     * 获取上传文件的完整路径
     *
     * @param bucketName 桶名称
     * @param fileName   文件名
     * @param expire     地址过期时间
     * @return
     */publicStringgetPresignedObjectUrl(String bucketName,String fileName,Integer expire){if(StringUtils.isEmpty(bucketName)){thrownewGeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}if(StringUtils.isEmpty(fileName)){thrownewGeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}
        expire =Objects.isNull(expire)? minioProperties.getExpire(): expire;// 验证桶是否存在在finalboolean validationBucket =bucketExists(bucketName);if(!validationBucket){thrownewGeneralException(ExceptionEnums.BUCKET_NOT_EXIST.getMsg());}// 验证文件是否存在finalboolean validationFileName =doFileNameExist(bucketName, fileName);if(!validationFileName){thrownewGeneralException(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 目录路径
     */publicMap<String,String>putDirObject(String bucketName,String objectName)throwsIOException,InvalidKeyException,InvalidResponseException,InsufficientDataException,NoSuchAlgorithmException,ServerException,InternalException,XmlParserException,ErrorResponseException{// 判断桶是否存在if(!bucketExists(bucketName)){thrownewGeneralException(ExceptionEnums.BUCKET_NAME_NOT_EXIST.getMsg());}finalObjectWriteResponse response = minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(newByteArrayInputStream(newbyte[]{}),0,-1).build());Map<String,String> map =newHashMap<>();
        map.put("etag", response.etag());
        map.put("versionId", response.versionId());return map;}/**
     * 判断桶是否存在
     *
     * @param bucketName 存储桶
     * @param objectName 文件夹名称(去掉/)
     * @return true:存在
     */publicbooleandoFolderExist(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:存在
     */publicbooleandoFileNameExist(String bucketName,String fileName){if(StringUtils.isEmpty(bucketName)){thrownewGeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}if(StringUtils.isEmpty(fileName)){thrownewGeneralException(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
     */publicvoiddownloadFile(HttpServletResponse response,String bucketName,String fileName){if(StringUtils.isEmpty(bucketName)){thrownewGeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}if(StringUtils.isEmpty(fileName)){thrownewGeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}// 判断文件是否存在finalboolean flag =doFileNameExist(bucketName, fileName);if(!flag){thrownewGeneralException(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   文件名称
     *                   说明:当前方法不能真正删除,需要验证
     */publicvoiddeleteFile(String bucketName,String fileName){if(StringUtils.isEmpty(bucketName)){thrownewGeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}if(StringUtils.isEmpty(fileName)){thrownewGeneralException(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  文件名
     */publicvoiddeleteBatchFile(String bucketName,List<String> fileNames){if(StringUtils.isEmpty(bucketName)){thrownewGeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}if(CollectionUtils.isEmpty(fileNames)){thrownewGeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}try{List<DeleteObject> objects =newLinkedList<>();for(String fileName : fileNames){
                objects.add(newDeleteObject(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
     */privatestaticStringformatFileSize(long fileS){DecimalFormat df =newDecimalFormat("#.00");String fileSizeString ="";String wrongSize ="0B";if(fileS ==0){return wrongSize;}if(fileS <1024){
            fileSizeString = df.format((double) fileS)+" B";}elseif(fileS <1048576){
            fileSizeString = df.format((double) fileS /1024)+" KB";}elseif(fileS <1073741824){
            fileSizeString = df.format((double) fileS /1048576)+" MB";}else{
            fileSizeString = df.format((double) fileS /1073741824)+" GB";}return fileSizeString;}}

7、文件调用接口

上传文件,获取上传文件完整路径,文件下载,文件删除,批量删除

importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.util.StringUtils;importorg.springframework.web.bind.annotation.*;importorg.springframework.web.multipart.MultipartFile;importjavax.servlet.http.HttpServletResponse;importjava.util.List;importjava.util.Map;@RestControllerpublicclassMinioController{@AutowiredprivateMinioUtils minioUtils;/**
     * 获取桶中文件名和大小列表
     *
     * @return
     */@GetMapping("/getFileList")publicList<Object>getFileList(){return minioUtils.getFileList("test",true);}/**
     * 判断文件是否存在
     *
     * @param bucketName
     * @param fileName
     * @return
     */@GetMapping("/doFileNameExist")publicbooleandoFileNameExist(String bucketName,String fileName){return minioUtils.doFolderExist(bucketName, fileName);}/**
     * 上传文件
     *
     * @param file
     * @return
     */@PostMapping("/uploadFiles")publicMap<String,Object>uploadFiles(String bucketName,@RequestParam(name ="file", required =false)MultipartFile[] file){if(file ==null|| file.length ==0){thrownewGeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());}if(StringUtils.isEmpty(bucketName)){thrownewGeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());}return minioUtils.uploadFile(bucketName, file);}/**
     * 获取上传文件的完整浏览路径
     *
     * @param filename
     * @return
     */@GetMapping("/getPresignedObjectUrl")publicStringgetPresignedObjectUrl(@RequestParam(name ="filename")String filename){return minioUtils.getPresignedObjectUrl("test", filename,null);}/**
     * 文件下载
     *
     * @param response
     * @param fileName
     */@GetMapping("/downloadFile")publicvoiddownloadFile(HttpServletResponse response,@RequestParam("fileName")String fileName){
        minioUtils.downloadFile(response,"test", fileName);}/**
     * 删除单个文件
     *
     * @param fileName 完整路径(不包含bucket)
     */@DeleteMapping("/deleteFile")publicvoiddeleteFile(String bucketName,String fileName){
        minioUtils.deleteFile(bucketName, fileName);}/**
     * 批量删除文件
     *
     * @param fileNames 完整路径(不包含bucket)
     */@DeleteMapping("/deleteBatchFile")publicvoiddeleteBatchFile(String bucketName,@RequestParam("fileNames")List<String> fileNames){
        minioUtils.deleteBatchFile(bucketName, fileNames);}}
标签: spring boot 后端 java

本文转载自: https://blog.csdn.net/qq_43108153/article/details/136610813
版权归原作者 师小师 所有, 如有侵权,请联系我们删除。

“SpringBoot整合Minio的详细步骤”的评论:

还没有评论