最新技术:使用 MINIO 文件服务器上传文件并返回URL

2022-07-28,,,,

因为项目中用到了 minIO 文件服务器,自己的话也是第一次接触,算是特别基础的东西,自己记录一下,以便以后随取随用

1、spring boot 整合 minIO

1-1 引入依赖

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

1-2 工具类

工具类是方便 操作 minIO

MinIOUtil

import io.minio.MinioClient;
import io.minio.errors.InvalidEndpointException;
import io.minio.errors.InvalidPortException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDate;
import java.util.Random;

/**
 * 对象存储工具类
 * @author 
 */
public class MinIOUtil {
    private static final  Logger log = LoggerFactory.getLogger(MinIOUtil.class);
    /**
     *  Mino服务器的AccessKey
     */
    private  final transient  static   String ACCESS_KEY = "minio 用户名";
    /**Mino服务器的SecretKey
     *
     */
    private  final transient  static  String SECRET_KEY = "密码";
    /**
     * 桶名称
     */
    private  final transient  static  String BUCKET_NAME ="桶名称";
    /**
     * minio 服务器地址
     */
    private  final transient  static   String OOS_URL ="minio 地址";
    /**
     * minio服务端口,默认是9000
     */
    private final transient static int OSS_PORT = 8057;
    private transient static boolean BUCKET_EXISTS = false;

    //单例模式-内部类实现
    private static class MinioClientHolder {
        private static MinioClient minioClient;

        static {
            try {
                minioClient = new MinioClient(OOS_URL, OSS_PORT, ACCESS_KEY, SECRET_KEY);
            } catch (InvalidEndpointException e) {
                e.printStackTrace();
            } catch (InvalidPortException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 获取minio客户端实例
     *
     * @return
     */
    public static MinioClient getMinioClient() {
        return MinioClientHolder.minioClient;
    }

    /**
     * 获取文件名
     *
     * @param muFile   文件
     * @param isRetain 是否保留源文件名
     * @return 返回文件名,以当前年月日作为前缀路径
     */
    private static String getFilePathName(MultipartFile muFile, boolean isRetain) {
        String fileName = muFile.getOriginalFilename();
        String name = fileName;
        String prefix = "";
        if (fileName.indexOf('.') != -1) {
            name = fileName.substring(0, fileName.indexOf('.'));
            prefix = fileName.substring(fileName.lastIndexOf("."));
        }

        LocalDate date = LocalDate.now();
        StringBuilder filePathName = new StringBuilder("/upload/");
        filePathName.append(date.getYear());
        filePathName.append("/");
        filePathName.append(date.getMonthValue());
        filePathName.append("/");
        filePathName.append(date.getDayOfMonth());
        filePathName.append("/");
        //添加随机后缀
        Random r = new Random();
        int pix = r.ints(1, (100 + 1)).findFirst().getAsInt();
        filePathName.append(System.currentTimeMillis());
        filePathName.append("" + pix);
        //文件名超过32字符则截取
        if (isRetain) {
            filePathName.append("_");
            if (name.length() >= 32) {
                name = name.substring(0, 32);
            }
            filePathName.append(name);
        }
        filePathName.append(prefix);
        return filePathName.toString();
    }
}

2、 具体实现

具体在接口里实现

@PostMapping("/uploadFile")
    @ApiOperation(value = "上传文件(通用:支持图片、音频、视频、文档)")
    public BaseResponse MinIOUploadFile(@ApiParam(name = "file", value = "文件", required = true)
                                       @RequestParam("file") MultipartFile file) {
        if (file.isEmpty() || file.getSize() == 0) {
            return setResultError("文件为空");
        }
        // 测试判断文件类型
        String type = VerifyFileType.gitFileTypeBySuffix(file);
        logger.info("type={}",type);

        try {
            MinioClient minioClient = MinIOUtil.getMinioClient();

            //是否存在名为“actives”的bucket
            if (!minioClient.bucketExists("actives")) {
                minioClient.makeBucket("actives");
            }
            String fileName = file.getOriginalFilename();
            String newName=null;
            if ("image".equals(type)){
                //图片
                 newName ="img/"+ UUID.randomUUID().toString().replaceAll("-", "")
                        + fileName.substring(fileName.lastIndexOf("."));
            }else if ("audio".equals(type)){
                 newName ="audio/"+ UUID.randomUUID().toString().replaceAll("-", "")
                        + fileName.substring(fileName.lastIndexOf("."));
            }else if ("video".equals(type)){
                 newName ="video/"+ UUID.randomUUID().toString().replaceAll("-", "")
                        + fileName.substring(fileName.lastIndexOf("."));
            }else if ("doc".equals(type)){
                newName ="doc/"+ UUID.randomUUID().toString().replaceAll("-", "")
                        + fileName.substring(fileName.lastIndexOf("."));
            }else {
                return  setResultError("该文件类型不能上传!");
            }
            //新的名称,pic会是bucket下的文件夹
            //获取file的inputStream
            InputStream inputStream = file.getInputStream();
            //上传
            minioClient.putObject("actives", newName, inputStream, "application/octet-stream");
            inputStream.close();
            //文件访问路径
            String url = minioClient.getObjectUrl("actives", newName);
            return setResultSuccess(url);
        } catch (Exception e) {
            logger.error(String.valueOf(e));
        }
        return setResultError("上传失败");
    }

之前因为业务只有 图片上传,所以就只写了图片上传的,后来需求增加一个 MP3上传预览的,索性就开发了一个 通用的,支持图片、音频、视频、文档

其中加了一个文件类型判断 ,区分文件类型分别以建立不同的文件夹存储

VerifyFileType.java

/**
     * 根据文件后缀进行分类
     * @param file
     * @return
     */
    public static String gitFileTypeBySuffix(MultipartFile file){
        String fileName = file.getOriginalFilename();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        // 文件类型分类
        String[] imgTypes = new String[] { "jpg", "png", "gif"};
        String[] audioTypes = new String[] { "wav", "mp3"};
        String[] videoTypes = new String[] { "avi", "mp4","rmvb","wmv","flv"};
        String[] docTypes = new String[] { "pdf", "docx","doc"};
        if (isValid(suffix, imgTypes)){
            return "image";
        }else if (isValid(suffix, audioTypes)){
            return  "audio";
        }else if (isValid(suffix,videoTypes)){
            return "video";
        }else if (isValid(suffix,docTypes)){
            return "doc";
        }else {
            return null;
        }
    }

 public static boolean isValid(String contentType, String... allowTypes) {
        if (null == contentType || "".equals(contentType)) {
            return false;
        }
        for (String type : allowTypes) {
            if (contentType.equals(type)) {
                return true;
            }
        }
        return false;
    }

网上还有一个 根据 fileCode 区分的,比较安全,有兴趣可以去看看,也蛮有意思的

上传完之后,返回的url 需在 html 标签中预览

<!DOCTYPE HTML>
<html>
<body>

音频
<audio src="http://ip/actives/mp3/0769d7ff6ae543ba94c2e0107a877f8f.mp3" controls="controls">
Your browser does not support the audio element.
</audio>
<br/>
图片
<img  src="http://ip/actives/img/253f5f6638cc4b419277ccb1538304b9.jpg"  alt="图片" height="200" width="200" />
<br/>
视频
<video src="http://ip/actives/video/8593052c6e784a1c8bdd593eb8db4ce5.mp4" controls="controls">
</video>
</body>
</html>

如果你 预览不行的话 看看minio 文件夹的权限有没有开

这样设置一下就ok 啦,

看看效果

《最新技术:使用 MINIO 文件服务器上传文件并返回URL.doc》

下载本文的Word格式文档,以方便收藏与打印。