Spring Boot 整合 Minio

一、导入依赖

复制代码
<!-- MinIO 客户端 -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.5.7</version>
</dependency>

<!-- OkHttp 是一个高效的网络库 -->
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.9.3</version>
</dependency>

二、添加yaml配置

复制代码
server:
  port: 3333

spring:
  servlet:
    multipart:
      max-request-size: 200MB
      max-file-size: 200MB

minio:
  url: http://127.0.0.1:19000 #换成自己的minio服务端地址
  accessKey: minioadmin # 用户名
  secretKey: minioadmin # 密码
  bucketName: demo  # bucketName指的就是之前创建的MinIO桶Bucket

三、创建配置类

复制代码
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @ClassName MinioConfig
 * @Description 文件
 * @Date 2024/9/21 10:21
 */
@Data
@Configuration
public class MinioConfig {
    /** 访问地址 */
    @Value("${minio.url}")
    private String endpoint;

    /** 唯一标识账户 */
    @Value("${minio.accessKey}")
    private String accessKey;

    /** 账户密码 */
    @Value("${minio.secretKey}")
    private String secretKey;

    /** 默认储存桶 */
    @Value("${minio.bucketName}")
    private String bucketName;

    /**
     * 标记此方法为一个 Bean,Spring 会在上下文中管理这个 Bean
     */
    @Bean
    public MinioClient minioClient(){
         使用 MinioClient 的构建器模式创建一个 MinioClient 实例
        return MinioClient.builder()
                //设置 Minio 服务的端点地址
                .endpoint(endpoint)
                // 设置访问 Minio 服务所需的访问密钥和秘密密钥
                .credentials(accessKey, secretKey)
                // 构建并返回 MinioClient 实例
                .build();
    }
}

四、创建工具类

java 复制代码
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;

/**
 * Minio工具类
 */
@Log4j2
@Component
@RequiredArgsConstructor
public class MinioUtils {
    /**
     * 定义一个私有的、不可变的 MinioClient 实例变量 minioClient
     */
    private final MinioClient minioClient;

    /**
     * 启动SpringBoot容器的时候初始化Bucket(桶)
     * 如果没有Bucket(桶)则创建
     *
     * @param bucketName Bucket(桶)名称
     */
    @SneakyThrows(Exception.class)
    private void createBucket(String bucketName) {
        if (!bucketExists(bucketName)) {
            //使用 minioClient 的 makeBucket 方法创建一个新的桶。
            //MakeBucketArgs.builder().bucket(bucketName).build() 创建了一个请求参数对象,指定了要创建的桶的名称。
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 判断Bucket(桶)是否存在
     *
     * @param bucketName Bucket(桶)名称
     * @return true:存在  false:不存在
     */
    @SneakyThrows(Exception.class)
    public boolean bucketExists(String bucketName) {
        //调用 minioClient 对象的 bucketExists 方法,检查指定的桶是否存在。
        //BucketExistsArgs.builder().bucket(bucketName).build() 创建了一个请求参数对象,用于传递桶名称。
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 获得Bucket(桶)的策略
     *
     * @param bucketName Bucket(桶)名称
     * @return 指定桶的访问策略
     */
    @SneakyThrows(Exception.class)
    public String getBucketPolicy(String bucketName) {
        //通过 MinIO 客户端 API 获取指定桶的访问策略。
        return minioClient.getBucketPolicy(
                //使用构建者模式创建一个 GetBucketPolicyArgs 对象,以便设置请求参数。
                GetBucketPolicyArgs
                        .builder()
                        //设置要获取策略的桶的名称。
                        .bucket(bucketName)
                        //构建并返回 GetBucketPolicyArgs 对象,该对象将作为参数传递给 getBucketPolicy 方法。
                        .build()
        );
    }

    /**
     * 获得所有Bucket(桶)列表
     *
     * @return 所有桶的列表
     */
    @SneakyThrows(Exception.class)
    public List<Bucket> getAllBuckets() {
        //调用 MinIO 客户端的 listBuckets 方法,返回当前用户拥有的所有桶的列表。
        return minioClient.listBuckets();
    }

    /**
     * 根据bucketName获取其相关信息
     *
     * @param bucketName Bucket(桶)名称
     * @return Optional<Bucket>
     */
    @SneakyThrows(Exception.class)
    public Optional<Bucket> getBucket(String bucketName) {
        //查找并返回指定名称的桶,如果不存在,则返回 Optional.empty()。
        //调用 getAllBuckets() 方法获取所有桶,然后使用 Java 8 的 Stream API 进行处理。
        //filter 方法用于筛选出名称与 bucketName 匹配的桶。
        //findFirst 方法用于返回第一个匹配的桶,返回值是一个 Optional<Bucket>,表示可能存在的桶。
        return getAllBuckets().stream().filter(bucket -> bucket.name().equals(bucketName)).findFirst();
    }

    /**
     * 根据bucketName删除Bucket(桶)  true:删除成功  false:删除失败
     *
     * @param bucketName Bucket(桶)名称
     */
    @SneakyThrows(Exception.class)
    public void removeBucket(String bucketName) {
        //调用 MinIO 客户端的 removeBucket 方法,通过构建 RemoveBucketArgs 对象来指定要删除的桶名。
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

   
    /**
     * 判断文件是否存在
     *
     * @param bucketName Bucket(桶)名称
     * @param objectName 文件名称
     * @return true:存在  false:不存在
     */
    public boolean isObjectExist(String bucketName, String objectName) {
        boolean exist = true;
        try {
            //调用 MinIO 客户端的 statObject 方法,构建 StatObjectArgs 对象以检查对象的状态。如果对象存在,该方法不会抛出异常。
            minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            log.error("【Minio工具类】>>>> 判断文件是否存在, 异常:", e);
            exist = false;
        }
        return exist;
    }

    /**
     * 判断文件夹是否存在
     *
     * @param bucketName Bucket(桶)名称
     * @param objectName 文件夹名称
     * @return true:存在  false:不存在
     */
    public boolean isFolderExit(String bucketName, String objectName) {
        boolean exist = true;
        try {
            //使用 listObjects 方法列出指定桶中以 objectName 为前缀的对象。recursive(false) 表示不进行递归查找。
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs
                            .builder()
                            .bucket(bucketName)
                            .prefix(objectName)
                            .recursive(false)
                            .build()
            );
            //遍历结果,检查每个 Item 是否为目录且名称与 objectName 匹配。如果找到匹配的目录,将 exist 设为 true。
            for (Result<Item> result : results) {
                Item item = result.get();
                if (item.isDir() && objectName.equals(item.objectName())) {
                    exist = true;
                }
            }
        } catch (Exception e) {
            log.error("【Minio工具类】>>>> 判断文件夹是否存在, 异常:", e);
            exist = false;
        }
        return exist;
    }

    /**
     * 根据文件前置查询文件
     *
     * @param bucketName 存储桶
     * @param prefix     前缀
     * @param recursive  是否使用递归查询
     * @return MinioItem 列表
     */
    @SneakyThrows(Exception.class)
    public List<Item> getAllObjectsByPrefix(String bucketName,
                                            String prefix,
                                            boolean recursive) {
        List<Item> list = new ArrayList<>();// 创建一个空的 Item 列表,用于存储结果
        Iterable<Result<Item>> objectsIterator = minioClient.listObjects(// 获取指定桶中以 prefix 开头的对象列表
                ListObjectsArgs.builder()// 构建 ListObjectsArgs 对象
                        .bucket(bucketName)// 设置桶名
                        .prefix(prefix)// 设置对象前缀
                        .recursive(recursive)// 设置是否递归查找
                        .build());// 构建参数
        if (objectsIterator != null) { // 检查对象迭代器是否不为 null
            for (Result<Item> o : objectsIterator) { // 遍历迭代器中的每个结果
                Item item = o.get();// 获取当前结果中的 Item 对象
                list.add(item);// 将 Item 添加到列表中
            }
        }
        return list;// 返回包含所有找到的 Item 的列表
    }

    /**
     * 获取文件流
     *
     * @param bucketName 存储桶
     * @param objectName 文件名
     * @return 二进制流
     */
    @SneakyThrows(Exception.class)
    public InputStream getObject(String bucketName, String objectName) {
        return minioClient.getObject(// 调用 MinIO 客户端的 getObject 方法,获取指定对象的输入流
                GetObjectArgs.builder()// 构建 GetObjectArgs 对象
                        .bucket(bucketName)// 设置桶名
                        .object(objectName)// 设置对象名
                        .build()); // 构建参数并调用 getObject
    }

    /**
     * 断点下载
     *
     * @param bucketName 存储桶
     * @param objectName 文件名称
     * @param offset     起始字节的位置
     * @param length     要读取的长度
     * @return 二进制流
     */
    @SneakyThrows(Exception.class)
    public InputStream getObject(String bucketName, String objectName, long offset, long length) {
        return minioClient.getObject(// 调用 MinIO 客户端的 getObject 方法,获取指定对象的输入流
                GetObjectArgs.builder()// 构建 GetObjectArgs 对象
                        .bucket(bucketName) // 设置桶名
                        .object(objectName)// 设置对象名
                        .offset(offset)// 设置读取的起始偏移量
                        .length(length) // 设置要读取的字节长度
                        .build());// 构建参数并调用 getObject
    }

    /**
     * 获取路径下文件列表
     *
     * @param bucketName 存储桶
     * @param prefix     文件名称
     * @param recursive  是否递归查找,false:模拟文件夹结构查找
     * @return 二进制流
     */
    public Iterable<Result<Item>> listObjects(String bucketName, String prefix, boolean recursive) {
        return minioClient.listObjects(// 调用 MinIO 客户端的 listObjects 方法,返回对象列表
                ListObjectsArgs.builder() // 构建 ListObjectsArgs 对象
                        .bucket(bucketName) // 设置桶名
                        .prefix(prefix)// 设置对象前缀
                        .recursive(recursive)// 设置是否递归查找
                        .build());// 构建参数并调用 listObjects
    }

    /**
     * 使用MultipartFile进行文件上传
     *
     * @param bucketName  存储桶
     * @param file        文件名
     * @param objectName  对象名
     * @param contentType 类型
     * @return
     */
    @SneakyThrows(Exception.class)
    public ObjectWriteResponse uploadFile(String bucketName, MultipartFile file, String objectName, String contentType) {
        InputStream inputStream = file.getInputStream(); // 获取文件的输入流,以便上传
        return minioClient.putObject(// 调用 MinIO 客户端的 putObject 方法,上传文件
                PutObjectArgs.builder()// 构建 PutObjectArgs 对象
                        .bucket(bucketName)// 设置桶名
                        .object(objectName)// 设置对象名
                        .contentType(contentType)// 设置文件的内容类型
                        .stream(inputStream, inputStream.available(), -1) // 设置输入流及其可用字节数,-1 表示不限制
                        .build());// 构建参数并调用 putObject
    }

    /**
     * 图片上传
     *
     * @param bucketName  存储桶
     * @param imageBase64 图像的 Base64 编码字符串
     * @param imageName   接收图像的原始名称
     * @return
     */
    public ObjectWriteResponse uploadImage(String bucketName, String imageBase64, String imageName) {
        if (!StringUtils.isEmpty(imageBase64)) {// 检查 Base64 字符串是否非空
            InputStream in = base64ToInputStream(imageBase64);// 将 Base64 字符串转换为输入流
            String newName = System.currentTimeMillis() + "_" + imageName + ".jpg";// 生成新文件名,包含当前时间戳和原始名称
            String year = String.valueOf(new Date().getYear());// 获取当前年份
            String month = String.valueOf(new Date().getMonth());// 获取当前月份
            return uploadFile(bucketName, year + "/" + month + "/" + newName, in); // 调用 uploadFile 方法上传文件,路径为 年/月/新文件名

        }
        return null;// 如果 Base64 字符串为空,返回 null
    }

    // BASE64Decoder在jdk8以上的版本移除了,报错最简单解决换成jdk8就行了
    public static InputStream base64ToInputStream(String base64) {
        ByteArrayInputStream stream = null;// 声明 ByteArrayInputStream 变量,用于保存字节流
        try {
            // 使用 BASE64Decoder 解码 Base64 字符串并去除首尾空格,得到字节数组
            byte[] bytes = new BASE64Decoder().decodeBuffer(base64.trim());
            // 将字节数组转换为 ByteArrayInputStream,以便返回
            stream = new ByteArrayInputStream(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return stream;// 返回字节输入流,如果出错则返回 null
    }


    /**
     * 上传本地文件
     *
     * @param bucketName 存储桶
     * @param objectName 对象名称
     * @param fileName   本地文件路径
     * @return
     */
    @SneakyThrows(Exception.class)
    public ObjectWriteResponse uploadFile(String bucketName, String objectName, String fileName) {
        return minioClient.uploadObject(// 调用 MinIO 客户端的 uploadObject 方法进行文件上传
                UploadObjectArgs.builder()// 使用 UploadObjectArgs 构建上传参数
                        .bucket(bucketName)// 设置存储桶名称
                        .object(objectName)// 设置对象名称(在存储桶中的文件名)
                        .filename(fileName)// 设置要上传的本地文件名
                        .build()); // 构建并返回 UploadObjectArgs 对象,然后执行上传
    }

    /**
     * 通过流上传文件
     *
     * @param bucketName  存储桶
     * @param objectName  文件对象
     * @param inputStream 文件流
     * @return
     */
    @SneakyThrows(Exception.class)
    public ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) {
        return minioClient.putObject( // 调用 MinIO 客户端的 putObject 方法进行文件上传
                PutObjectArgs.builder()// 使用 PutObjectArgs 构建上传参数
                        .bucket(bucketName)// 设置存储桶名称
                        .object(objectName)// 设置对象名称(在存储桶中的文件名)
                        .stream(inputStream, inputStream.available(), -1)// 设置输入流及其大小,-1 表示使用默认值
                        .build()); // 构建并返回 PutObjectArgs 对象,然后执行上传
    }

    /**
     * 创建文件夹或目录
     *
     * @param bucketName 存储桶
     * @param objectName 目录路径
     * @return
     */
    @SneakyThrows(Exception.class)
    public ObjectWriteResponse createDir(String bucketName, String objectName) {
        return minioClient.putObject(// 调用 MinIO 客户端的 putObject 方法进行上传
                PutObjectArgs.builder()// 使用 PutObjectArgs 构建上传参数
                        .bucket(bucketName)// 设置存储桶名称
                        .object(objectName) // 设置对象名称(在存储桶中的"目录"名称)
                        .stream(new ByteArrayInputStream(new byte[]{}), 0, -1)// 使用空的字节数组流,表示创建一个空对象,0 表示长度,-1 表示使用默认值
                        .build());// 构建并返回 PutObjectArgs 对象,然后执行上传
    }

    /**
     * 获取文件信息, 如果抛出异常则说明文件不存在
     *
     * @param bucketName 存储桶
     * @param objectName 文件名称
     * @return
     */
    @SneakyThrows(Exception.class)
    public String getFileStatusInfo(String bucketName, String objectName) {
        return minioClient.statObject( // 调用 MinIO 客户端的 statObject 方法获取对象状态信息
                StatObjectArgs.builder() // 使用 StatObjectArgs 构建请求参数
                        .bucket(bucketName) // 设置存储桶名称
                        .object(objectName)// 设置对象名称
                        .build() // 构建并返回 StatObjectArgs 对象
        ).toString();// 调用 toString 方法,将获取的状态信息转换为字符串并返回
    }

    /**
     * 拷贝文件
     *
     * @param bucketName    存储桶
     * @param objectName    文件名
     * @param srcBucketName 目标存储桶
     * @param srcObjectName 目标文件名
     */
    @SneakyThrows(Exception.class)
    public ObjectWriteResponse copyFile(String bucketName, String objectName, String srcBucketName, String srcObjectName) {
        return minioClient.copyObject(// 调用 MinIO 客户端的 copyObject 方法进行对象复制
                CopyObjectArgs.builder()// 使用 CopyObjectArgs 构建复制参数
                        .source(CopySource.builder()// 设置复制源
                                .bucket(bucketName) // 源存储桶名称
                                .object(objectName)// 源对象名称
                                .build())// 构建并返回 CopySource 对象
                        .bucket(srcBucketName)// 设置目标存储桶名称
                        .object(srcObjectName) // 设置目标对象名称
                        .build() // 构建并返回 CopyObjectArgs 对象
        );// 执行对象复制并返回 ObjectWriteResponse
    }

    /**
     * 删除文件
     *
     * @param bucketName 存储桶
     * @param objectName 文件名称
     */
    @SneakyThrows(Exception.class)
    public void removeFile(String bucketName, String objectName) {
        minioClient.removeObject(// 调用 MinIO 客户端的 removeObject 方法以删除指定对象
                RemoveObjectArgs.builder()// 使用 RemoveObjectArgs 构建删除参数
                        .bucket(bucketName) // 设置要删除对象的存储桶名称
                        .object(objectName)// 设置要删除的对象名称
                        .build());// 构建并返回 RemoveObjectArgs 对象,并执行删除操作
    }

    /**
     * 批量删除文件
     *
     * @param bucketName 存储桶
     * @param keys       需要删除的文件列表
     * @return
     */
    public void removeFiles(String bucketName, List<String> keys) {
        List<DeleteObject> objects = new LinkedList<>();// 创建一个 LinkedList 用于存储待删除对象
        keys.forEach(s -> {// 遍历对象键列表
            objects.add(new DeleteObject(s)); // 将每个键封装成 DeleteObject 并添加到列表中
            try {
                // 尝试执行删除操作
                removeFile(bucketName, s);// 调用 removeFile 方法删除指定对象
            } catch (Exception e) {
                log.error("【Minio工具类】>>>> 批量删除文件,异常:", e);
            }
        });
    }

    /**
     * 获取文件外链
     * 使用了 .expiry(expires) 方法,指定了预签名 URL 的过期时间。
     * 适用于需要限制 URL 有效期的场景。
     * 适用于需要设定过期时间的情况
     *
     * @param bucketName 存储桶
     * @param objectName 文件名
     * @param expires    过期时间 <=7 秒 (外链有效时间(单位:秒))
     * @return url
     */
    @SneakyThrows(Exception.class)
    public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) {
        // 构建获取预签名对象 URL 的参数
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()// 使用 GetPresignedObjectUrlArgs 的构建器
                .expiry(expires)// 设置 URL 的过期时间(以秒为单位)
                .bucket(bucketName) // 设置存储桶名称
                .object(objectName)// 设置对象名称
                .build();// 构建并返回 GetPresignedObjectUrlArgs 对象
        return minioClient.getPresignedObjectUrl(args);// 调用 MinIO 客户端获取预签名 URL 并返回
    }

    /**
     * 获得文件外链
     * 使用了 .method(Method.GET) 方法,指定了 HTTP 请求方法为 GET。
     * 适用于需要明确请求类型的场景,但没有设置过期时间。
     * 适用于无需设定过期时间的访问
     *
     * @param bucketName
     * @param objectName
     * @return url
     */
    @SneakyThrows(Exception.class)
    public String getPresignedObjectUrl(String bucketName, String objectName) {
        // 构建获取预签名对象 URL 的参数
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()// 使用 GetPresignedObjectUrlArgs 的构建器
                .bucket(bucketName) // 设置存储桶名称
                .object(objectName)// 设置对象名称
                .method(Method.GET)// 设置对象名称
                .build();// 构建并返回 GetPresignedObjectUrlArgs 对象
        return minioClient.getPresignedObjectUrl(args);// 调用 MinIO 客户端获取预签名 URL 并返回
    }

    /**
     * 将URLDecoder编码转成UTF8
     *
     * @param str
     * @return
     * @throws UnsupportedEncodingException
     */
    public String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException {
        // 将字符串中的不合法 URL 编码转换为合法编码
        String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25");// 使用正则表达式,将不符合 URL 编码规范的百分号(%)替换为 %25
        return URLDecoder.decode(url, "UTF-8");// 使用 URLDecoder 解码字符串,返回 UTF-8 编码的结果
    }

    
}

一些解释

1.@Log4j2:这是一个 Lombok 提供的注解,用于自动生成一个 Log4j2 日志记录器。使用此注解后,你可以直接调用 log 对象来记录日志,而无需手动创建日志实例。

2.@Component:这是 Spring 框架的注解,表示该类是一个 Spring 组件,Spring 会自动检测并将其注册为一个 Bean。可以用于自动装配和依赖注入。

3.@RequiredArgsConstructor:也是 Lombok 提供的注解,自动生成一个构造函数,该构造函数接受所有被 final 修饰的字段或带有 @NonNull 注解的字段。这简化了依赖注入的过程,使代码更加简洁。

4.@SneakyThrows(Exception.class) :是 Lombok 提供的一个注解,用于在方法上处理检查性异常。

作用:

自动处理异常:在使用 @SneakyThrows 注解的方法中,如果抛出任何类型的检查性异常(即编译器要求显式捕获或声明的异常),Lombok 会自动将其包装成运行时异常(RuntimeException),从而避免了需要显式捕获或声明这些异常的麻烦。

注意事项:

尽管 @SneakyThrows 使代码更简洁,但在调试或异常处理时,要小心隐式处理异常可能导致的问题。如果可能,考虑在应用程序中适当处理异常,以提高代码的可读性和可维护性。

5‌‌.Optional‌是Java 8引入的一个新的容器对象,它提供了非常丰富的API,主要是为了解决空指针异常的问题。Optional类允许你创建一个可能为null的值的容器,从而避免了直接使用null值可能导致的空指针异常。它提供了多种方法来操作这个容器,包括判断值是否存在(isPresent())、获取值(get())、转换值(map())、过滤值(filter())等。

6.OkHttp 是一个高效的 HTTP 客户端库,用于发送和接收网络请求,它支持同步和异步调用,自动处理常见的网络问题如重定向、HTTPS握手和连接池。它广泛用于 Android 和 Java 应用程序中,以提高网络通信的效率和稳定性。

五、创建controller层

java 复制代码
/**
 * Minio控制层
 */
@Log4j2
@RestController
@RequestMapping("/minio")
public class MinioController {

    @Autowired
    private MinioUtils minioUtils;

    @Autowired
    private MinioConfig minioConfig;

    /**
     * 文件上传
     *
     * @param file
     */
    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file) {
        try {
            //文件名
            String fileName = file.getOriginalFilename();
            String newFileName = System.currentTimeMillis() + "." + StringUtils.substringAfterLast(fileName, ".");
            //类型
            String contentType = file.getContentType();
            minioUtils.uploadFile(minioConfig.getBucketName(), file, newFileName, contentType);
            return "上传成功";
        } catch (Exception e) {
            log.error("上传失败",e);
            return "上传失败";
        }
    }

    /**
     * 删除
     *
     * @param fileName
     */
    @DeleteMapping("/")
    public void delete(@RequestParam("fileName") String fileName) {
        minioUtils.removeFile(minioConfig.getBucketName(), fileName);
    }

    /**
     * 获取文件信息
     *
     * @param fileName
     * @return
     */
    @GetMapping("/info")
    public String getFileStatusInfo(@RequestParam("fileName") String fileName) {
        return minioUtils.getFileStatusInfo(minioConfig.getBucketName(), fileName);
    }

    /**
     * 获取文件外链
     *
     * @param fileName
     * @return
     */
    @GetMapping("/url")
    public String getPresignedObjectUrl(@RequestParam("fileName") String fileName) {
        return minioUtils.getPresignedObjectUrl(minioConfig.getBucketName(), fileName);
    }

    /**
     * 文件下载
     *
     * @param fileName
     * @param response
     */
    @GetMapping("/download")
    public void download(@RequestParam("fileName") String fileName, HttpServletResponse response) {
        try {
            // 从 MinIO 获取指定的对象(文件)的输入流
            InputStream fileInputStream = minioUtils.getObject(minioConfig.getBucketName(), fileName);
            // 设置响应头,指明这是一个附件下载,并指定下载文件的名称
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            // 设置响应的内容类型为强制下载
            response.setContentType("application/force-download");
            // 设置响应的字符编码为 UTF-8
            response.setCharacterEncoding("UTF-8");
            // 将输入流中的文件内容复制到响应输出流中
            IOUtils.copy(fileInputStream, response.getOutputStream());
            response.flushBuffer(); // 刷新响应输出流
        } catch (Exception e) {
            log.error("下载失败");
            System.out.println(e);
        }
    }
相关推荐
一屉大大大花卷17 分钟前
初识Neo4j之入门介绍(一)
数据库·neo4j
凯基迪科技20 分钟前
exe软件壳的分类----加密保护壳
java
wuxuanok32 分钟前
Web后端开发-分层解耦
java·笔记·后端·学习
周胡杰1 小时前
鸿蒙arkts使用关系型数据库,使用DB Browser for SQLite连接和查看数据库数据?使用TaskPool进行频繁数据库操作
前端·数据库·华为·harmonyos·鸿蒙·鸿蒙系统
wkj0011 小时前
navicate如何设置数据库引擎
数据库·mysql
ladymorgana1 小时前
【Spring Boot】HikariCP 连接池 YAML 配置详解
spring boot·后端·mysql·连接池·hikaricp
赵渝强老师1 小时前
【赵渝强老师】Oracle RMAN的目录数据库
数据库·oracle
暖暖木头1 小时前
Oracle注释详解
数据库·oracle
kyle~1 小时前
C/C++字面量
java·c语言·c++
neoooo1 小时前
别慌,Java只有值传递——一次搞懂“为啥我改了它还不变”!
java·后端·spring