SpringBoot 集成 Minio (附带工具类)

Minio

官方文档: https://www.minio.org.cn/docs/minio/container/index.html

MinIO是一个对象存储解决方案,它提供了与Amazon Web Services S3兼容的API,并支持所有核心S3功能。 MinIO有能力在任何地方部署 - 公有云或私有云,裸金属基础设施,编排环境,以及边缘基础设施。

项目集成

1. 引入依赖

<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.5.17</version>
</dependency>

2. Yaml配置

minio:
  endpoint: http://127.0.0.1:9090  # 访问地址,取决于部署的ip和端口
  accessKey: minioadmin
  secretKey: minioadmin
  bucketName: xxxx    # Minio创建的桶名称

3. 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;

/**
 * @author 98
 * @date 2025-03-19 09:28
 */
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioConfiguration {

    private String endpoint;

    private String accessKey;

    private String secretKey;

    private String bucketName;

    @Bean
    public MinioClient minioClient(){
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }

}

4. 工具类

/**
 * @author 98
 * @date 2025-03-19 09:30
 */
@Component
public class MinioUtils {

    private static final Log log = LogFactory.get();

    /**
     * 默认大小 50M
     */
    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;

    /**
     * 默认的文件名最大长度 100
     */
    public static final int DEFAULT_FILE_NAME_LENGTH = 100;

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfiguration minioConfiguration;


    /**
     * 判断bucket是否存在,不存在则创建
     * @param bucketName
     */
    public void existBucket(String bucketName) {
        try {
            BucketExistsArgs build = BucketExistsArgs.builder().bucket(bucketName).build();
            boolean exists = minioClient.bucketExists(build);
            if (!exists) {
                this.makeBucket(bucketName);
            }
        } catch (Exception e) {
            log.error("bucket判断失败", e);
            throw ExceptionFactory.getBusinessException(HttpStatus.HTTP_INTERNAL_ERROR, e.toString());
        }
    }

    /**
     * 创建存储bucket
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            log.error("bucket创建失败", e);
            throw ExceptionFactory.getBusinessException(HttpStatus.HTTP_INTERNAL_ERROR, e.toString());
        }
        return true;
    }

    /**
     * 删除存储bucket
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            log.error("bucket删除失败", e);
            throw ExceptionFactory.getBusinessException(HttpStatus.HTTP_INTERNAL_ERROR, e.toString());
        }
        return true;
    }

    /**
     * 单个上传文件
     * @param multipartFile
     * @return
     */
    public String upload(MultipartFile multipartFile) throws Exception {
        // 校验文件
        assertAllowed(multipartFile, MimeTypeConstant.DEFAULT_ALLOWED_EXTENSION);
        // 判断桶是否存在
        this.existBucket(minioConfiguration.getBucketName());

        String fileName = extractFilename(multipartFile);

        InputStream inputStream = null;
        try {
            inputStream = multipartFile.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioConfiguration.getBucketName())
                    .object(fileName)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(multipartFile.getContentType())
                    .build()
            );
        } catch (IOException e) {
            log.error("上传文件失败", e.toString());
            throw ExceptionFactory.getBusinessException(HttpStatus.HTTP_INTERNAL_ERROR, e.toString());
        } finally {
            if (Objects.nonNull(inputStream)) {
                inputStream.close();
            }
        }
        return fileName;
    }

    /**
     * 获取Minio浏览地址
     * @param fileName
     * @return
     */
    public String getReviewUrl(String fileName) {
        String url = null;
        try {
            url = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(minioConfiguration.getBucketName())
                            .object(fileName)
                            .expiry(7, TimeUnit.DAYS)
                            .build()
            );
        } catch (Exception e) {
            log.error("获取浏览地址失败", e.toString());
            throw ExceptionFactory.getBusinessException(HttpStatus.HTTP_INTERNAL_ERROR, e.toString());
        }
        return url;
    }

    /**
     * 文件校验
     * @param file
     * @param allowedExtension
     */
    private static void assertAllowed(MultipartFile file, String[] allowedExtension) throws InvalidExtensionException {
        if (file.getSize() > DEFAULT_MAX_SIZE) {
            long size = DEFAULT_MAX_SIZE / 1024 / 1024;
            throw ExceptionFactory.getBusinessException(HttpStatus.HTTP_INTERNAL_ERROR, "文件大小超过限制(MB):" + size);
        }
        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);

        if (Objects.nonNull(allowedExtension) && !isAllowedExtension(extension, allowedExtension)) {
            if (allowedExtension == MimeTypeConstant.IMAGE_EXTENSION) {
                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
                        fileName);
            } else if (allowedExtension == MimeTypeConstant.FLASH_EXTENSION) {
                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
                        fileName);
            } else if (allowedExtension == MimeTypeConstant.MEDIA_EXTENSION) {
                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
                        fileName);
            } else if (allowedExtension == MimeTypeConstant.VIDEO_EXTENSION) {
                throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
                        fileName);
            } else {
                throw new InvalidExtensionException(allowedExtension, extension, fileName);
            }
        }
    }

    /**
     * 获取文件后缀
     * @param file
     * @return
     */
    private static String getExtension(MultipartFile file) {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StrUtil.isBlank(extension)) {
            String contentType = Optional.ofNullable(file.getContentType()).orElse(StrUtil.EMPTY);
            extension = MimeTypeConstant.getExtension(contentType);
        }
        return extension;
    }

    /**
     * 判断MIME类型是否是允许的MIME类型
     * @param extension
     * @param allowedExtension
     * @return
     */
    private static boolean isAllowedExtension(String extension, String[] allowedExtension) {
        for (String str : allowedExtension) {
            if (str.equalsIgnoreCase(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 自定义文件名
     * @param file
     * @return
     */
    private static String extractFilename(MultipartFile file) {
        String extension = getExtension(file);
        return DateUtil.today() + "/" + UUID.fastUUID() + "." + extension;
    }

    /**
     * description: 下载文件
     *
     * @param fileName
     * @return: org.springframework.http.ResponseEntity<byte[]>
     */
    public ResponseEntity<byte[]> download(String fileName) {
        ResponseEntity<byte[]> responseEntity = null;
        GetObjectArgs build = GetObjectArgs.builder().bucket(minioConfiguration.getBucketName()).object(fileName).build();

        try (InputStream in = minioClient.getObject(build); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Collections.singletonList("*"));
            responseEntity = new ResponseEntity<>(bytes, headers, HttpStatus.HTTP_OK);
        } catch (Exception e) {
            log.error("下载失败", e.toString());
            throw ExceptionFactory.getBusinessException(HttpStatus.HTTP_INTERNAL_ERROR, e.toString());
        }
        return responseEntity;
    }
}
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import java.util.Arrays;

/**
 * @author 98
 * @date 2025-03-19 09:40
 */
public class InvalidExtensionException extends FileUploadException {

    private static final long serialVersionUID = 1L;

    private String[] allowedExtension;
    private String extension;
    private String filename;

    public InvalidExtensionException(String[] allowedExtension, String extension, String filename) {
        super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]");
        this.allowedExtension = allowedExtension;
        this.extension = extension;
        this.filename = filename;
    }

    public String[] getAllowedExtension() {
        return allowedExtension;
    }

    public String getExtension() {
        return extension;
    }

    public String getFilename() {
        return filename;
    }

    public static class InvalidImageExtensionException extends InvalidExtensionException {
        private static final long serialVersionUID = 1L;

        public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) {
            super(allowedExtension, extension, filename);
        }
    }

    public static class InvalidFlashExtensionException extends InvalidExtensionException {
        private static final long serialVersionUID = 1L;

        public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) {
            super(allowedExtension, extension, filename);
        }
    }

    public static class InvalidMediaExtensionException extends InvalidExtensionException {
        private static final long serialVersionUID = 1L;

        public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) {
            super(allowedExtension, extension, filename);
        }
    }

    public static class InvalidVideoExtensionException extends InvalidExtensionException {
        private static final long serialVersionUID = 1L;

        public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) {
            super(allowedExtension, extension, filename);
        }
    }
}
/**
 * @author 98
 * @date 2025-03-19 09:37
 */
public class MimeTypeConstant {

    public static final String IMAGE_PNG = "image/png";

    public static final String IMAGE_JPG = "image/jpg";

    public static final String IMAGE_JPEG = "image/jpeg";

    public static final String IMAGE_BMP = "image/bmp";

    public static final String IMAGE_GIF = "image/gif";

    public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };

    public static final String[] FLASH_EXTENSION = { "swf", "flv" };

    public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
            "asf", "rm", "rmvb" };

    public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };

    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
            // 图片
            "bmp", "gif", "jpg", "jpeg", "png",
            // word excel powerpoint
            "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
            // 压缩文件
            "rar", "zip", "gz", "bz2",
            // 视频格式
            "mp4", "avi", "rmvb",
            // pdf
            "pdf" };

    public static String getExtension(String prefix) {
        switch (prefix) {
            case IMAGE_PNG:
                return "png";
            case IMAGE_JPG:
                return "jpg";
            case IMAGE_JPEG:
                return "jpeg";
            case IMAGE_BMP:
                return "bmp";
            case IMAGE_GIF:
                return "gif";
            default:
                return "";
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值