Spring Boot 集成 MinIO 实现文件上传

Spring Boot 集成 MinIO 实现文件上传

一、 Minio 服务准备

MinIO的搭建过程参考 Docker 搭建 MinIO 对象存储。

登录MinIO控制台,新建一个 Bucket,修改 Bucket 权限为公开。

在这里插入图片描述

二、MinIO 集成

  1. 添加 MinIO 依赖
<!-- https://mvnrepository.com/artifact/io.minio/minio -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>${minio.version}</version>
</dependency>
  1. 在项目配置文件application.yml中添加自定义配置。properties 文件自行转换
minio:
  host: http://【服务器公网ip】:【minio运行端口号,默认9000】/
  access-key: 账号
  secret-key: 密码
  1. 创建配置文件类
@Data
@Component
public class MinioConfig {
 
    @Value(value = "${minio.host}")
    private String host;
 
    @Value(value = "${minio.access-key}")
    private String accessKey;
 
    @Value(value = "${minio.secret-key}")
    private String secretKey;

    @Bean
    public MinioClient minioClient(){

        return MinioClient.builder()
                .endpoint(host)
                .credentials(accessKey, secretKey)
                .build();
    }
}
  1. 创建文件上传工具类
@Component
@Slf4j
@AllArgsConstructor
public class MinioUtils {

    private final MinioClient minioClient;
    private final MinioConfig minioConfig;

    /**
     * 初始化Bucket
     */
    private void createBucket(String bucketName) {
        // 设置公开读写
        String POLICY_PATTERN = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::%s/*\"]}]}";
        try {
            // 判断 BucketName 是否存在
            if (!bucketExists(bucketName)) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
            minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(
                    String.format(POLICY_PATTERN, bucketName)
            ).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 验证bucketName是否存在
     *
     * @return boolean true:存在
     */
    public boolean bucketExists(String bucketName) {
        if (StringUtils.isBlank(bucketName)) {
            throw new ServerException(ErrorCode.BUCKET_NAME_NOT_NULL);
        }
        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 ServerException(ErrorCode.BUCKET_NAME_NOT_NULL);
        }
        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 bucketName 存储桶名称
     * @param file       file
     * @return map
     */
    public Map<String, Object> uploadFile(String bucketName, MultipartFile[] file) {
        if (file == null || file.length == 0) {
            throw new ServerException(ErrorCode.FILE_NAME_NOT_NULL);
        }
        createBucket(bucketName);

        List<String> urlList = new ArrayList<>(file.length);
        for (MultipartFile multipartFile : file) {
            String originFileName = multipartFile.getOriginalFilename();
            if (StringUtils.isBlank(originFileName)) {
                throw new ServerException(ErrorCode.FILE_NAME_NOT_NULL);
            }
            String[] originFileNameArr = originFileName.split("\\.");
            String suffix = originFileNameArr[originFileNameArr.length - 1];
            String newFileName = UUID.randomUUID().toString().replace("-", "").concat(".").concat(suffix);
            urlList.add(String.format("%s%s/%s", minioConfig.getHost(), bucketName, newFileName));
            try {
                // 文件上传
                InputStream in = multipartFile.getInputStream();
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(newFileName)
                        .stream(in, multipartFile.getSize(), -1)
                        .contentType(multipartFile.getContentType())
                        .build());
                in.close();
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
        Map<String, Object> data = new HashMap<>();
        data.put("bucketName", bucketName);
        data.put("urlList", urlList);
        return data;
    }

    /**
     * 获取上传文件的完整路径
     *
     * @param bucketName 桶名称
     * @param fileName   文件名
     * @return
     */
    public String getPresignedObjectUrl(String bucketName, String fileName) {
        if (StringUtils.isEmpty(bucketName)) {
            throw new ServerException(ErrorCode.BUCKET_NAME_NOT_NULL);
        }
        if (StringUtils.isEmpty(fileName)) {
            throw new ServerException(ErrorCode.FILE_NAME_NOT_NULL);
        }
        // 验证桶是否存在在
        final boolean validationBucket = bucketExists(bucketName);
        if (!validationBucket) {
            throw new ServerException(ErrorCode.BUCKET_NOT_EXIST);
        }
        // 验证文件是否存在
        final boolean validationFileName = doFileNameExist(bucketName, fileName);
        if (!validationFileName) {
            throw new ServerException(ErrorCode.FILE_NOT_EXIST);
        }
        String url = null;
        try {
            // 获取桶和文件的完整路径
            url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .method(Method.GET)
                    .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 Exception {
        // 判断桶是否存在
        if (!bucketExists(bucketName)) {
            throw new ServerException(ErrorCode.BUCKET_NAME_NOT_EXIST);
        }
        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 fileName 对象
     * @return true:存在
     */
    public boolean doFileNameExist(String bucketName, String fileName) {
        if (StringUtils.isEmpty(bucketName)) {
            throw new ServerException(ErrorCode.BUCKET_NAME_NOT_NULL);
        }
        if (StringUtils.isEmpty(fileName)) {
            throw new ServerException(ErrorCode.FILE_NAME_NOT_NULL);
        }
        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 ServerException(ErrorCode.BUCKET_NAME_NOT_NULL);
        }
        if (StringUtils.isEmpty(fileName)) {
            throw new ServerException(ErrorCode.FILE_NAME_NOT_NULL);
        }
        // 判断文件是否存在
        final boolean flag = doFileNameExist(bucketName, fileName);
        if (!flag) {
            throw new ServerException(ErrorCode.FILE_NOT_EXIST);
        }
        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 ServerException(ErrorCode.BUCKET_NAME_NOT_NULL);
        }
        if (StringUtils.isEmpty(fileName)) {
            throw new ServerException(ErrorCode.FILE_NAME_NOT_NULL);
        }
        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 ServerException(ErrorCode.BUCKET_NAME_NOT_NULL);
        }
        if (CollectionUtils.isEmpty(fileNames)) {
            throw new ServerException(ErrorCode.FILE_NAME_NOT_NULL);
        }
        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) {
            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;
    }
}

三、上传文件实战

新建 UploadController,实现上传文件接口。

@Tag(name = "基础接口")
@AllArgsConstructor
@RestController
@RequestMapping("/file")
public class UploadController {
    private final MinioUtils minioUtils;

    @PostMapping("upload")
    @Operation(summary = "上传文件")
    public Result<Map<String, Object>> upload(@RequestParam(defaultValue = "common") String bucketName,
                                   @RequestParam(name = "file", required = false) MultipartFile[] file) {
        return Result.ok(minioUtils.uploadFile(bucketName, file));
    }
}

调用上传文件接口后,系统会根据 bucketName 首先判断 bucket 是否存在,不存在则会开始创建,并且设置成公共读写。然后遍历文件数组,对文件重命名,并且记录下上传后的文件访问 url。最后进行文件上传。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/729983.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

难辨真假的Midjourney案例(附提示词):适合练手

人物 时尚女孩 Street style fashion photo, full-body shot of a young Chinese woman with long curly black hair, walking confidently with a crowd of people down a sidewalk in Hong Kong, wearing a emerald green Gucci maxi dress & gold jewelry, sunset lig…

英伟达和IBM搞事情!主攻“量子计算+AI”

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨娴睿/慕一 排版丨沛贤 深度好文&#xff1a;2000字丨8分钟阅读 Ismael Faro是一位计算机工程师&#xff0c;自2015年以来&#xff0c;他就成为开发IBM量子软件生态系统的重要人物。从2016…

2748. 美丽下标对的数目(Rust暴力枚举)

题目 给你一个下标从 0 开始的整数数组 nums 。如果下标对 i、j 满足 0 ≤ i < j < nums.length &#xff0c;如果 nums[i] 的 第一个数字 和 nums[j] 的 最后一个数字 互质 &#xff0c;则认为 nums[i] 和 nums[j] 是一组 美丽下标对 。 返回 nums 中 美丽下标对 的总…

DualSPHysics运行报错ERROR: Some boundary particle was excluded.

如下查看输出&#xff0c;看到报错ERROR: Some boundary particle was excluded.某些边界粒子超出了模拟域的X限制&#xff08;右限制&#xff09;&#xff0c;具体错误的边界溢出粒子储存在Error_BoundaryOut.vtk里边。 用paraview打开Error_BoundaryOut.vtk还有边界的stl&am…

React路由笔记(函数组件,自用)

配置 npm i react-router-dom基本使用 目录结构 在src中创建page文件夹放置各页面组件&#xff0c;router中放置路由 1、router中配置路由 在/router/index.js中&#xff0c;使用createBrowserRouter配置路由。 import { createBrowserRouter } from "react-router…

mybatis框架相关问题总结(本地笔记搬运)

1、背景 2、运行启动问题 问题一 运行spring boot项目时报错&#xff1a;‘factoryBeanObjectType‘: java.lang.String 解决一 版本问题&#xff0c;springframework版本和mybatis/mybatis-plus版本不兼容。现spring-boot使用3.3.0版本&#xff0c;mybatis-plus使用3.5.7…

js处理数据(过滤)

复选框的值这里为true或false 选中为true&#xff0c;未选中为false 看看数据&#xff1a; type中的前面那些字母是固定的不会变 括号里面的不固定&#xff0c;那就把固定的作为前缀去过滤&#xff0c;后面怎么变都无所谓&#xff0c;当checkbox三个值中的某个或某些值为false时…

如何使用LiveTargetsFinder生成实时活动主机URL列表

关于LiveTargetsFinder LiveTargetsFinder是一款功能强大的实时活动主机生成工具&#xff0c;该工具可以为广大研究人员以自动化的形式生成可供分析和测试的实时活动主机URL列表&#xff0c;并通过MassDNS、Masscan和Nmap自动过滤出无法访问的主机。 我们只需要提供一个域名作…

视频融合共享平台LntonCVS视频监控管理平台技术方案详细介绍

LntonCVS国标视频综合管理平台是一款以视频为核心的智慧物联应用平台。它基于分布式、负载均衡等流媒体技术进行开发&#xff0c;提供广泛兼容、安全可靠、开放共享的视频综合服务。该平台具备多种功能&#xff0c;包括视频直播、录像、回放、检索、云存储、告警上报、语音对讲…

【数据分享】《中国改革年鉴》1989-2022

最近老有同学过来询问《中国经济体制改革年鉴》、《中国改革年鉴》这两本数据的关系以及怎么获取这两本本数据。今天就在这里给大家分享一下这三本数据的具体情况。 《中国改革年鉴》由国家发展和改革委员会主管,中国经济体制改革研究会主办,中国经济体制改革杂志社编辑出版,是…

大数据存储技术笔记

目录 大数据的特性 HDFS 读流程的基本步骤 HDFS 写流程的基本步骤 Mapreduce的执行过程 MapReduce 中 combiner 作用 hadoop 调度器及其工作方法 Hive 中内部表与外部表区别(创建删除角度) Hadoop 的 2 个主要组件及其功能 Hadoop MapReduce 的工作流程 正常工作的 ha…

Jmeter性能 之 “查看结果树” 界面功能介绍

前言 查看结果树 显示所有请求响应的树&#xff0c;通过它可以查看任何请求的响应。除了显示响应之外&#xff0c;还可以查看获取响应所花费的时间以及一些响应代码。需要通过"查看结果树"来查看服务器处理请求之后的返回结果&#xff0c;分析是否存在问题 注意&am…

VoIP Hopper一键分析VoIP 网络信息(KALI工具系列二十九)

目录 1、KALI LINUX 简介 2、VoIP Hopper工具简介 3、信息收集 3.1 目标主机IP 3.2kali的IP 4、操作实例 4.1 VLAN 跳跃攻击 4.2 指定MAC地址 4.3 设置参数 5、总结 1、KALI LINUX 简介 Kali Linux 是一个功能强大、多才多艺的 Linux 发行版 &#xff0c;广泛用于网络…

【深度学习】GPT1,提高语言理解的生成预训练方法

论文&#xff1a; https://s3-us-west-2.amazonaws.com/openai-assets/research-covers/language-unsupervised/language_understanding_paper.pdf 文章目录 提高语言理解的生成预训练方法摘要引言相关工作自然语言处理的半监督学习无监督预训练辅助训练目标 框架无监督预训练有…

VMware Workstation安装Windows Server2019系统详细操作步骤

虚拟机版本 VMware Workstation 16 Prp 16.2.5 build-20904516 实现操作 创建虚拟机 创建新的虚拟机 自定义->下一步 默认即可&#xff0c;下一步 稍后安装操作系统->下一步 按照图下所示选择好系统->下一步 设置好虚拟机名称和位置->下一步 默认即可&#xff0…

大数据学习-环境准备

VMware 部分 网络设置 下载好 CentOS 7 的镜像文件 修改 VMware 的网络 把子网 ip 修改为 192.168.88.0&#xff0c;然后点击 NAT 设置&#xff0c;修改网关 IP 为 192.168.88.2 之后就确定即可 虚拟机安装 选择镜像文件&#xff0c;使用 VMware 的典型安装方法即可&#…

jQuery 基本操作

01-简介 jQuery 是一个功能丰富且广泛使用的 JavaScript 库&#xff0c;它简化了 HTML 文档遍历和操作、事件处理、动画和 Ajax 操作。jQuery 通过其易用的 API&#xff0c;使复杂的 JavaScript 编程任务变得更加简单&#xff0c;并且兼容各种浏览器。 1、jQuery特点 简化 DOM …

智慧工厂监控可视化解决方案(160页WORD)

方案介绍&#xff1a; 本智慧工厂监控可视化解决方案通过集成先进的物联网和大数据技术&#xff0c;为制造业企业提供了全面的数字化转型支持。通过实时监控、数据分析、可视化展示等功能&#xff0c;帮助企业提升生产效率、降低运营成本、优化产品质量和能源利用率&#xff0…

设计模式(三)代理模式

目录 一、什么是代理模式 二、静态代理 1、定义 2、代码 2.1、接口 2.2、被代理对象 2.3、代理对象 2.4、测试 三、动态代理 1、定义 2、代码 2.1、接口 2.2、目标对象 2.3、代理对象 2.4、测试 一、什么是代理模式 代理模式(Proxy Pattern)是一种结构性模式。代理模…

注塑件检测视觉检测中可能遇到的外观缺陷

机器视觉检测注塑件不良特征有哪些&#xff1f;按照检测需求一般分为两类&#xff1a;外观缺陷和尺寸缺陷。但由于注塑件的工艺特点及原材料特性&#xff0c;注塑件外观缺陷在生产过程中出现的概率于频率远远大于尺寸缺陷。 注塑件检测视觉检测中可能遇到的外观缺陷 1、色差&a…