构建spring boot web项目:七、集成minio

目录

一、docker安装minio

二、springboot整合minio

2.1minio准备

2.1.1配置 ACCESS KEYS 获取

2.1.2新建桶,用来存放文件的位置

2.2 创建minio模块

2.3 引入依赖

2.4配置minio

三、管理应用模块(management)集成minio

四、启动时报错

一、docker安装minio

1.1拉取镜像

docker pull minio/minio 

1.2创建挂载目录

mkdir -p /home/minio/config
mkdir -p /home/minio/data

1.3启动容器

docker run -p 9000:9000 -p 9090:9090 --name minio -d --restart=always -e "MINIO_ACCESS_KEY=minioadmin" -e "MINIO_SECRET_KEY=minioadmin" -v /home/minio/data:/data -v /home/minio/config:/root/.minio  minio/minio server /data --console-address ":9090" -address ":9000"
命令描述
-d --restart=always这是运行容器的其他选项,-d使容器在后台运行,–restart=always表示容器总是会在退出后自动重启
-e “MINIO_ACCESS_KEY=minioadmin”用户名
-e “MINIO_SECRET_KEY=minioadmin”密码

minio/minio server <br/>/data --console-address ":9090" -address ":9000    

这是容器内要运行的命令,启动一个名为 “minio” 的服务器,数据存储在 /data 目录下,服务器的控制台地址为 “:9090”,服务地址为 “:9000”

查看容器

访问操作
http://ip:9090/login  账号:minioadmin 密码:minioadmin

二、springboot整合minio

minio基本概念

  1. bucket(桶) :类似文件系统的目录(文件夹);
  2. Object : 类似文件系统的文件;
  3. Keys :类似文件名;
  4. MINIO_ACCESS_KEY:访问key,类似账号;
  5. MINIO_SECRET_KEY:秘钥,类似密码。

2.1minio准备

2.1.1配置 ACCESS KEYS 获取

新建用户

2.1.2新建桶,用来存放文件的位置

2.2 创建minio模块

2.3 引入依赖

pom.xml(minio)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.lyj.initMode</groupId>
        <artifactId>initMode-function</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>initMode-function-minio</artifactId>

    <description>minio功能模块</description>

    <dependencies>
        <!--文件存储minio-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
        </dependency>
        <dependency>
            <groupId>com.lyj.initMode</groupId>
            <artifactId>initMode-function-mysql</artifactId>
        </dependency>
    </dependencies>

</project>

pom.xml(initMode)


        <!--minio 文件服务器-->
        <minio.version>7.1.0</minio.version>


            <dependency>
                <groupId>com.lyj.initMode</groupId>
                <artifactId>initMode-function-minio</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>${minio.version}</version>
            </dependency>

pom.xml(function)

<module>minio</module>

2.4配置minio

MinioConfig.java
package com.lyj.function.minio.config;

import com.lyj.common.base.enums.ErrorCodeEnum;
import com.lyj.common.base.util.BizExceptionUtil;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.errors.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

/**
 * minio配置类
 * @author faminefree
 */
@Slf4j
@Configuration
@EnableConfigurationProperties({com.lyj.function.minio.config.MinioProperties.class})
public class MinioConfig {

    @Bean
    public MinioClient minioClient(MinioProperties properties) {
        // 创建一个Minio的客户端对象
        MinioClient client = MinioClient.builder()
                .endpoint(properties.getEndpoint())
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();
        //创建一个Minio的存储桶
        BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder()
                .bucket(properties.getBucketName())
                .build();
        //判断桶是否存在
        try {
            if (!client.bucketExists(bucketExistsArgs)) {
                // 如果不存在,那么此时就创建一个新的桶
                MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder()
                        .bucket(properties.getBucketName()).build();
                client.makeBucket(makeBucketArgs);
            }
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidBucketNameException
                | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException
                | ServerException | XmlParserException | RegionConflictException e) {
            log.error(ErrorCodeEnum.MINIO_CONNECT_ERROR.getMessage(),e);
            BizExceptionUtil.bizException(ErrorCodeEnum.MINIO_CONNECT_ERROR);
        }
        return client;
    }
}
MinioProperties.java
package com.lyj.function.minio.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * minio配置属性类
 */
@Data
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {

    /**
     * 对象存储服务的URL
     * <pre>
     *     Examples:
     *         * https://s3.amazonaws.com
     *         * https://s3.amazonaws.com/
     *         * https://play.min.io
     *         * http://play.min.io:9010/
     *         * localhost
     *         * localhost.localdomain
     *         * play.min.io
     *         * 127.0.0.1
     *         * 192.168.1.60
     *         * ::1
     * </pre>
     */
    private String endpoint;
    /**
     * Access key 账户ID
     */
    private String accessKey;
    /**
     * Secret key 密码
     */
    private String secretKey;
    /**
     * 默认的存储桶名称
     */
    private String bucketName;

    /**
     * 允许的扩展名文件名,用逗号分隔,如果为空则没有限制
     * allowed extension filename,separate by comma,no limits if blank
     * <pre>
     *     Examples:
     *       txt,doc,docx
     * </pre>
     */
    private String extension;

    /**
     * 图片扩展
     * jpg,jpeg,png,bmp,gif
     */
    private String pictureExtension;

    /**
     * 图片限制文件大小
     * picture limit file size
     */
    private Integer pictureVerifySize;
}

 创建文件记录表(sys_file)

-- init.sys_file definition

CREATE TABLE `sys_file` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `attachment_suffix` varchar(20) NOT NULL COMMENT '附件后缀:docx/rar/zip/jpg/pdf等',
  `attachment_size` bigint NOT NULL COMMENT '文件大小 KB',
  `original_name` varchar(1024) NOT NULL COMMENT '原始文件名称',
  `file_no` varchar(128) DEFAULT NULL COMMENT '文件编号 每个文件都唯一,对外下载使用',
  `file_path` varchar(1024) DEFAULT NULL COMMENT 'minio完整路径',
  `create_by` bigint DEFAULT NULL COMMENT '创建人',
  `update_by` bigint DEFAULT NULL COMMENT '更新人',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `delete_flag` tinyint DEFAULT '0',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=97920 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='文件minio';

 使用EasyCode生成代码

BeanUtil.java(base)

package com.lyj.common.base.util;

import com.lyj.common.base.enums.ErrorCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

/**
 * bean工具类
 */
@Slf4j
public class BeanUtil {

    private BeanUtil() {
        super();
    }

    /**
     * 按照目标类型创建一个实例并复制相关属性
     *
     * @param sourceBean     源bean
     * @param targetBeanType 目标bean类型
     * @param <T>            目标bean类型
     * @return 目标bean实例
     */
    public static <T> T clone(Object sourceBean, Class<T> targetBeanType) {
        try {
            T targetBean = targetBeanType.newInstance();
            BeanUtils.copyProperties(sourceBean, targetBean);
            return targetBean;
        } catch (InstantiationException | IllegalAccessException e) {
            log.error("BeanUtil|clone|error|sourceBean={},targetBeanType={}", sourceBean, targetBeanType, e);
            BizExceptionUtil.bizException(ErrorCodeEnum.BEAN_CLONE_ERROR);
        }
        return null;
    }

    /**
     * 复制属性
     *
     * @param source 源bean
     * @param target 目标bean
     */
    public static void copy(Object source, Object target) {
        BeanUtils.copyProperties(source, target);
    }

    /**
     * 将page对象转换为目标对象
     *
     * @param page           page数据
     * @param targetBeanType 目标对象类型
     * @param <T>            目标对象类型
     * @return 目标对象page数据
     */
    public static <T> Page<T> convertPage(Page<?> page, Class<T> targetBeanType) {
        List<T> content = convertList(page.getContent(), targetBeanType);
        if (content == null) {
            content = new ArrayList<>();
        }
        return new PageImpl<>(content, page.getPageable(), page.getTotalElements());
    }


    /**
     * 将list对象转换为目标对象
     *
     * @param list           list数据
     * @param targetBeanType 目标对象类型
     * @param <T>            目标对象类型
     * @return 目标对象list数据
     */
    public static <T> List<T> convertList(List<?> list, Class<T> targetBeanType) {
        return list.stream().map(t -> BeanUtil.clone(t, targetBeanType)).collect(Collectors.toList());
    }


    public static void copyPropertiesIgnoreNull(Object source, Object target) throws BeansException {
        copyPropertiesIgnoreNull(source, target, null, (String[])null);
    }
    public static void copyPropertiesIgnoreNull(Object source, Object target, String... ignoreProperties) throws BeansException {
        copyPropertiesIgnoreNull(source, target, null, ignoreProperties);
    }
    private static void copyPropertiesIgnoreNull(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) throws BeansException {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> actualEditable = target.getClass();
        if (editable != null) {
            if (!editable.isInstance(target)) {
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
            }

            actualEditable = editable;
        }

        PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);
        List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
        PropertyDescriptor[] var7 = targetPds;
        int var8 = targetPds.length;

        for(int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor targetPd = var7[var9];
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }

                            Object value = readMethod.invoke(source);
                            if (value != null) {
                                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                    writeMethod.setAccessible(true);
                                }
                                writeMethod.invoke(target, value);
                            }
                        } catch (Throwable var15) {
                            throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
                        }
                    }
                }
            }
        }

    }

    public static String[] getNullPropertyNames (Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<String>();
        for(PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) emptyNames.add(pd.getName());
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

}
DeleteFlagEnum.java(base)

package com.lyj.common.base.enums;

import lombok.Getter;

/**
 * 删除标记
 */
@Getter
public enum DeleteFlagEnum {

    /**
     * 删除标记
     */
    NO(0, "未删除"),
    YES(1, "已删除");

    private final Integer code;
    private final String message;

    DeleteFlagEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

minio服务service

MinioFileService.java
package com.lyj.function.minio.service;

import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;

/**
 * @author lxr
 * @Description minio操作
 * @date Created in 2022/3/17
 */
public interface MinioFileService {

    /**
     * 下载
     */
    InputStream download(String filePath);


    /**
     * 上传
     *
     * @return 文件id
     */
    Boolean upload(MultipartFile file, String filePath);

    /**
     * 上传
     *
     * @return 文件id
     */
    Boolean upload(File file, String filePath);

    /**
     * 删除
     *
     * @return
     */
    Boolean delete(String filePath);

    String picturePreview(String filePath);

}
MinioServiceImpl.java
package com.lyj.function.minio.service.impl;


import cn.hutool.core.collection.CollUtil;
import com.lyj.common.base.constant.CommonConstant;
import com.lyj.common.base.enums.ErrorCodeEnum;
import com.lyj.common.base.exceptions.BusinessException;
import com.lyj.common.base.util.BizExceptionUtil;
import com.lyj.function.minio.service.MinioFileService;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import com.lyj.function.minio.config.MinioProperties;

/**
 * <p>
 * 文件minio 服务实现类
 * </p>
 *
 * @author lxr
 * @since 2022-03-16
 */
@Slf4j
@Service
@AllArgsConstructor
public class MinioServiceImpl implements MinioFileService {

    private final MinioClient minioClient;
    private final MinioProperties properties;

    @Override
    public InputStream download(String filePath) {
        try {
            return minioClient.getObject(GetObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(filePath)
                    .build());
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidBucketNameException
                | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException
                | ServerException | XmlParserException e) {
            log.error(ErrorCodeEnum.MINIO_READ_ERROR.getMessage() + ":{}", filePath, e);
            BizExceptionUtil.bizException(ErrorCodeEnum.MINIO_READ_ERROR);
            return null;
        }
    }

    @Override
    public Boolean upload(MultipartFile file, String filePath) {
        try {
            return this.store(file.getOriginalFilename(), file.getSize(), filePath, file.getContentType(), file.getInputStream());
        } catch (IOException e) {
            log.error("minio upload error:{}", e.getMessage());
            return Boolean.FALSE;
        }
    }

    @Override
    public Boolean upload(File file, String filePath) {
        try {
//            return this.store(file.getName(), file.length(), filePath, Files.probeContentType(file.toPath()), new FileInputStream(file));
            return this.store(file.getName(), file.length(), filePath,new MimetypesFileTypeMap().getContentType(file), new FileInputStream(file));
        } catch (IOException e) {
            log.error("minio upload error:{}", e.getMessage());
            return Boolean.FALSE;
        }
    }

    @Override
    public Boolean delete(String filePath) {

        try {
            RemoveObjectArgs removeObjectArgs =
                    RemoveObjectArgs.builder()
                            .bucket(properties.getBucketName())
                            .object(filePath)
                            .build();
            minioClient.removeObject(removeObjectArgs);
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidBucketNameException
                | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException
                | ServerException | XmlParserException e) {
            log.error(ErrorCodeEnum.MINIO_WRITE_ERROR.getMessage() + ":{}", filePath, e);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    @Override
    public String picturePreview(String filePath){
        try {
            minioClient.statObject(StatObjectArgs.builder().bucket(properties.getBucketName()).object(filePath).build());
        } catch (Exception e) {
            //找不到文件则抛异常
            log.error(ErrorCodeEnum.MINIO_READ_ERROR.getMessage() + ":{}", filePath);
            return null;
        }
        GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder()
                .bucket(properties.getBucketName())
                .object(filePath)
                .method(Method.GET)
                .build();
        String url = null;
        try {
            url = minioClient.getPresignedObjectUrl(build);
        } catch (Exception e) {
            log.error("MinioServiceImpl|picturePreview|error, get url error, filePath:{}", filePath);
            return null;
        }
        log.info("MinioServiceImpl|picturePreview|info, filePath:{},url:{}", filePath,url);
        return url;
    }


    private Boolean store(String fileName, Long fileSize, String filePath, String contentType, InputStream input) {

        if ((fileName.length() * 2) > 255) {
            log.error(ErrorCodeEnum.MINIO_FILE_NAME_TOO_LONG.getMessage());
            throw new BusinessException(ErrorCodeEnum.MINIO_FILE_NAME_TOO_LONG.getMessage());
        }

        if (StringUtils.isNotBlank(properties.getExtension())) {
            // 不空时才进行格式校验
            List<String> leagueExtension = Arrays.stream(properties.getExtension().split(CommonConstant.COMMA))
                    .filter(StringUtils::isNotBlank)
                    .collect(Collectors.toList());
            if (!leagueExtension.isEmpty()) {
                // 不空时才进行格式校验
                String suffix = fileName.substring(Math.max(fileName.lastIndexOf(CommonConstant.DOT) + 1, 0));
                boolean match = false;
                for (String s : leagueExtension) {
                    if (StringUtils.isNotBlank(suffix) && suffix.equalsIgnoreCase(s)) {
                        match = true;
                    }
                }
                //校验图片文件大小
                if (StringUtils.isNotBlank(properties.getPictureExtension())) {
                    List<String> pictureExtension = Arrays.stream(properties.getPictureExtension().split(CommonConstant.COMMA))
                            .filter(StringUtils::isNotBlank)
                            .collect(Collectors.toList());
                    if (CollUtil.isNotEmpty(pictureExtension) && pictureExtension.contains(suffix) && properties.getPictureVerifySize() != null) {
                        if (BigDecimal.valueOf(fileSize)
                                .divide(BigDecimal.valueOf(1024 * 1024), 3, RoundingMode.HALF_UP).doubleValue() > properties.getPictureVerifySize()) {
                            BizExceptionUtil.bizException(ErrorCodeEnum.MINIO_PICTURE_FILE_SIZE_ERROR.getCode(), String.format(ErrorCodeEnum.MINIO_PICTURE_FILE_SIZE_ERROR.getMessage(), properties.getPictureVerifySize()));
                        }
                    }
                    if (!match) {
                        log.error(ErrorCodeEnum.MINIO_FILE_SUFFIX_ERROR.getMessage());
                        throw new BusinessException(ErrorCodeEnum.MINIO_FILE_SUFFIX_ERROR.getMessage());
                    }
                }
            }
        }


        PutObjectArgs putObjectArgs;
        try {
            putObjectArgs = PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(filePath)
                    .contentType(contentType)
                    .stream(input, input.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidBucketNameException
                | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException
                | ServerException | XmlParserException e) {
            log.error(ErrorCodeEnum.MINIO_WRITE_ERROR.getMessage() + ":{}", fileName, e);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

}
SysFileService.java
package com.lyj.function.minio.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.lyj.common.domain.sysFile.entity.SysFile;
import com.lyj.common.domain.sysFile.vo.SysFileDetailVO;
import com.lyj.common.domain.sysFile.vo.SysFileVO;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.List;

/**
 * <p>
 * 文件minio 服务类
 * </p>
 *
 * @author lxr
 * @since 2022-03-16
 */
public interface SysFileService extends IService<SysFile> {

    void downLoad(HttpServletResponse response, Long fileId);

    /**
     * 带时间戳的下载
     * @param response
     * @param fileId
     */
    void downLoadWithTimestamp(HttpServletResponse response, Long fileId);

    byte[] downLoad(Long fileId);

    Long upload(MultipartFile file, Long userId);

    Long upload(File file, Long userId);

    /**
     * 文件详情
     *
     * @param fileIdList
     * @return
     */
    List<SysFileVO> detail(List<Long> fileIdList);
    List<SysFileDetailVO> detailAddUrl(List<Long> fileIdList);

    /**
     * 图片预览
     * @param filePath
     * @return url
     */
    String picturePreview(String filePath);

}
SysFileServiceImpl.java
package com.lyj.function.minio.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lyj.common.base.enums.DeleteFlagEnum;
import com.lyj.common.base.enums.ErrorCodeEnum;
import com.lyj.common.base.exceptions.BusinessException;
import com.lyj.common.base.util.BeanUtil;
import com.lyj.common.base.util.BizExceptionUtil;
import com.lyj.common.base.util.DateUtil;
import com.lyj.common.domain.sysFile.entity.SysFile;
import com.lyj.common.domain.sysFile.vo.SysFileDetailVO;
import com.lyj.common.domain.sysFile.vo.SysFileVO;
import com.lyj.function.minio.service.MinioFileService;
import com.lyj.function.minio.service.SysFileService;
import com.lyj.function.mysql.dao.sysFile.SysFileMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 文件minio 服务实现类
 * </p>
 *
 * @author lxr
 * @since 2022-03-16
 */
@Slf4j
@Service
@AllArgsConstructor
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements SysFileService {

    private static final String DOT = ".";
    private static final String PATH_SEPARATOR = "/";

    private final MinioFileService minioFileService;

    @Override
    public void downLoad(HttpServletResponse response, Long fileId) {
        SysFile byId = getById(fileId);
        if (Objects.isNull(byId)) {
            BizExceptionUtil.bizException("文件不存在!");
        }
        String fileName = byId.getOriginalName();

        InputStream inputStream = minioFileService.download(byId.getFilePath());

        response.setContentType("multipart/form-data");
        try {
            response.setHeader("Content-Disposition", "attachment;filename="
                    + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
        } catch (UnsupportedEncodingException e) {
            response.setHeader("Content-Disposition", "attachment;filename="
                    + new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
        }

        try (OutputStream os = response.getOutputStream()) {
            IOUtils.copy(inputStream, os);
            os.flush();
        } catch (IOException e) {
            throw new BusinessException(ErrorCodeEnum.DOWNLOAD_ERROR);
        }
    }

    @Override
    public void downLoadWithTimestamp(HttpServletResponse response, Long fileId) {
        SysFile byId = getById(fileId);
        if (Objects.isNull(byId)) {
            BizExceptionUtil.bizException("文件不存在!");
            return;
        }
        String fileName = byId.getOriginalName();
        if (fileName != null && fileName.contains(".")) {
            String[] split = fileName.split("\\.");
            fileName = split[0] + DateUtil.getLocalDateTime() + "." + split[1];
        }
        InputStream inputStream = minioFileService.download(byId.getFilePath());
        response.setContentType("multipart/form-data");
        try {
            response.setHeader("Content-Disposition", "attachment;filename="
                    + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
        } catch (UnsupportedEncodingException e) {
            response.setHeader("Content-Disposition", "attachment;filename="
                    + new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
        }

        try (OutputStream os = response.getOutputStream()) {
            IOUtils.copy(inputStream, os);
            os.flush();
        } catch (IOException e) {
            throw new BusinessException(ErrorCodeEnum.DOWNLOAD_ERROR);
        }
    }

    @Override
    public byte[] downLoad(Long fileId) {
        SysFile byId = getById(fileId);
        InputStream inputStream = minioFileService.download(byId.getFilePath());
        try {
            return IOUtils.toByteArray(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new byte[]{};
    }

    @Override
    public Long upload(MultipartFile file, Long userId) {
        String originalFilename = file.getOriginalFilename();
        String uuid = UUID.randomUUID().toString();
        String filePath = uuid + PATH_SEPARATOR + originalFilename;
        SysFile sysFile = new SysFile();
        sysFile.setAttachmentSuffix(originalFilename.substring(originalFilename.lastIndexOf(DOT)));
        sysFile.setAttachmentSize(file.getSize() / 8);
        sysFile.setOriginalName(originalFilename);
        sysFile.setFileNo(uuid);
        sysFile.setFilePath(filePath);
        sysFile.setDeleteFlag(DeleteFlagEnum.NO.getCode());
        sysFile.setCreateBy(userId);
        sysFile.setUpdateBy(userId);

        LocalDateTime now = LocalDateTime.now();
        sysFile.setCreateTime(now);
        sysFile.setUpdateTime(now);

        save(sysFile);

        minioFileService.upload(file, filePath);
        return sysFile.getId();
    }

    @Override
    public Long upload(File file, Long userId) {
        String originalFilename = file.getName();
        String uuid = UUID.randomUUID().toString();
        String filePath = uuid + PATH_SEPARATOR + originalFilename;
        SysFile sysFile = new SysFile();
        sysFile.setAttachmentSuffix(originalFilename.substring(originalFilename.lastIndexOf(DOT)));
        sysFile.setAttachmentSize(file.length() / 8);
        sysFile.setOriginalName(originalFilename);
        sysFile.setFileNo(uuid);
        sysFile.setFilePath(filePath);
        sysFile.setDeleteFlag(DeleteFlagEnum.NO.getCode());
        sysFile.setCreateBy(userId);
        sysFile.setUpdateBy(userId);

        LocalDateTime now = LocalDateTime.now();
        sysFile.setCreateTime(now);
        sysFile.setUpdateTime(now);

        save(sysFile);

        minioFileService.upload(file, filePath);
        return sysFile.getId();
    }

    @Override
    public List<SysFileVO> detail(List<Long> fileIdList) {
        List<SysFile> sysFiles = this.list(new LambdaQueryWrapper<SysFile>().in(SysFile::getId, fileIdList));
        List<SysFileVO> SysFileVOs = BeanUtil.convertList(sysFiles, SysFileVO.class);

        return SysFileVOs;
    }
    @Override
    public List<SysFileDetailVO> detailAddUrl(List<Long> fileIdList) {
        List<SysFile> sysFiles = this.list(new LambdaQueryWrapper<SysFile>().in(SysFile::getId, fileIdList));
        List<SysFileDetailVO> list = new ArrayList<>();
        for (SysFile sysFile : sysFiles) {
            SysFileDetailVO SysFileDetailVO = new SysFileDetailVO();
            BeanUtil.copy(sysFile,SysFileDetailVO);
            SysFileDetailVO.setUrl(picturePreviewBase64(sysFile.getFilePath()));
            list.add(SysFileDetailVO);
        }

        return list;
    }

    @Override
    public String picturePreview(String filePath){
        return minioFileService.picturePreview(filePath);

    }

    public String picturePreviewBase64(String filePath) {
        InputStream download = minioFileService.download(filePath);
        if (download!=null){
            byte[] data = null;
            try {
                ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
                byte[] buff = new byte[100];
                int rc = 0;
                while ((rc = download.read(buff, 0, 100)) > 0) {
                    swapStream.write(buff, 0, rc);
                }
                data = swapStream.toByteArray();
            } catch (Exception e) {
                log.error("PunchMonthReportQueryServiceImpl|info|error|read picture error e:{}", e);
            } finally {
                try {
                    download.close();
                } catch (Exception e) {
                    log.error("PunchMonthReportQueryServiceImpl|info|error|download close error e:{}", e);
                }
            }
            return Base64.getEncoder().encodeToString(data);
        }
        return null;
    }

}

三、管理应用模块(management)集成minio

pom.xml(manegement)


        <dependency>
            <groupId>com.lyj.initMode</groupId>
            <artifactId>initMode-function-minio</artifactId>
        </dependency>

application.yml

minio: #minio 参数
  accessKey: FjFA028O825BZ47II9fT
  secretKey: 5AumALef8wccA1caMamfD3U0DxLAQTK4Z54jQd3y
  # 存放文件的桶
  bucketName: test
  # 访问路径
  endpoint: http://192.168.163.158:9000

测试方法

    @GetMapping("/uploadFile")
    @ApiOperation("测试minio上传")
    public R<Long> uploadFile() {
        File file =  new File("D:下载/7-Zip 64位_23.1.0.0.exe");
        return R.ok(sysFileService.upload(file,123L));
    }

    @GetMapping(value = "/downLoad", produces = "application/octet-stream")
    @ApiOperation("测试minio下载")
    public void downLoad(HttpServletResponse response, Long fileId) {
        sysFileService.downLoad(response, fileId);
    }

启动服务测试

四、启动时报错

原因1:

使用的用户权限不足

原因2

minio系统时间与应用服务系统时间相差较大

使用命令同步时间

ntpdate pool.ntp.org

  • 24
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值