Spring Boot 集成 MinIO 对象存储服务器

MinIO 安装

wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
./minio server /data

启动成功:

[root@localhost minio]# ./minio server /data
Endpoint: http://***:9000  http://***:9000  http://127.0.0.1:9000     
RootUser: minioadmin 
RootPass: minioadmin 

Browser Access:
   http://***:9000  http://***:9000  http://127.0.0.1:9000    

Command-line Access: https://docs.min.io/docs/minio-client-quickstart-guide
   $ mc alias set myminio http://***:9000 minioadmin minioadmin

Object API (Amazon S3 compatible):
   Go:         https://docs.min.io/docs/golang-client-quickstart-guide
   Java:       https://docs.min.io/docs/java-client-quickstart-guide
   Python:     https://docs.min.io/docs/python-client-quickstart-guide
   JavaScript: https://docs.min.io/docs/javascript-client-quickstart-guide
   .NET:       https://docs.min.io/docs/dotnet-client-quickstart-guide
Detected default credentials 'minioadmin:minioadmin', please change the credentials immediately using 'MINIO_ROOT_USER' and 'MINIO_ROOT_PASSWORD'
IAM initialization complete

问题1:浏览器无法访问“http://***:9000”

需要开放端口如:

1、开启防火墙 
    systemctl start firewalld

2、开放指定端口
    firewall-cmd --zone=public --add-port=9000/tcp --permanent
     命令含义:
    --zone #作用域
    --add-port=9000/tcp  #添加端口,格式为:端口/通讯协议
    --permanent  #永久生效,没有此参数重启后失效

3、重启防火墙
    firewall-cmd --reload

4、查看端口号
	netstat -ntlp   //查看当前所有tcp端口·

	netstat -ntulp |grep 9000   //查看所有9000端口使用情况·

文档:http://docs.minio.org.cn/docs/

修改账户及密码

修改权限

chmod +x minio

执行命令:

export MINIO_ACCESS_KEY=accesskey(自行设置)
export MINIO_SECRET_KEY=secretkey(自行设置)
./minio server /data/bucket &

默认执行

[root@localhost ~]#cd /usr/local/minio
[root@localhost minio]# export MINIO_ACCESS_KEY=admin	
[root@localhost minio]# export MINIO_SECRET_KEY=admin
[root@localhost minio]# nohup ./minio server /data

查看进程
ps -ef|grep minio

Spring Boot 集成 minio 及使用

添加依赖

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

添加配置

package com.zyy.common.utils.minio;

/**
 * @author bfsz
 * @version 1.0
 * @description: Minio服务配置项
 * @date 2021/4/19 11:45
 */
public class MinioConfig {
    // "endPoint是一个URL,域名,IPv4或者IPv6地址"
    public static String endpoint = "http://xxx.xx.xx.xx/";
    // "TCP/IP端口号"
    public static int port = 9000;
    // "accessKey类似于用户ID,用于唯一标识你的账户"
    public static String accessKey = "";
    // "secretKey是你账户的密码"
    public static String secretKey = "";
    // "如果是true,则用的是https而不是http,默认值是true"
    public static Boolean secure = false;
    // "默认存储桶"
    public static String bucketName = "wypt";
}

Minio工具类

package com.zyy.utils.minio;

import cn.hutool.core.util.RandomUtil;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InvalidExpiresRangeException;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import net.coobird.thumbnailator.Thumbnails;
import net.minidev.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import static com.zyy.utils.thumbnailator.FileSuffixUtils.isPicture;


/**
 * @author bfsz
 * @version 1.0
 * @description: Minio工具类
 * @date 2021/4/19 11:49
 */

public class MinioUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(MinioUtil.class);
    //失效时间 默认7天
    private static final Integer DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;
    private static MinioClient minioClient;


    static {
        try {
            minioClient = new MinioClient(MinioConfig.endpoint, MinioConfig.port, MinioConfig.accessKey, MinioConfig.secretKey, MinioConfig.secure);
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("初始化minio配置异常: 【{}】", e.fillInStackTrace());
        }
    }

    /**
     * @description: 检查存储桶是否存在
     * @param: bucketName
     * @return: boolean
     * @author bfsz
     * @date: 2021/4/19 11:56
     */
    @SneakyThrows
    public boolean bucketExists(String bucketName) {
        boolean flag = false;
        flag = minioClient.bucketExists(bucketName);
        if (flag) {
            return true;
        }
        return false;
    }

    /**
     * @description: 创建存储桶
     * @param: bucketName
     * @return: boolean
     * @author bfsz
     * @date: 2021/4/19 11:57
     */
    @SneakyThrows
    public boolean makeBucket(String bucketName) {
        boolean flag = bucketExists(bucketName);
        if (!flag) {
            minioClient.makeBucket(bucketName);
            return true;
        } else {
            return false;
        }
    }

    /**
     * @description: 列出所有存储桶名称
     * @param:
     * @return: java.util.List<java.lang.String>
     * @author bfsz
     * @date: 2021/4/19 11:58
     */
    @SneakyThrows
    public List<String> listBucketNames() {
        List<Bucket> bucketList = listBuckets();
        List<String> bucketListName = new ArrayList<>();
        for (Bucket bucket : bucketList) {
            bucketListName.add(bucket.name());
        }
        return bucketListName;
    }

    /**
     * @description: 列出所有存储桶
     * @param:
     * @return: java.util.List<io.minio.messages.Bucket>
     * @author bfsz
     * @date: 2021/4/19 11:58
     */
    @SneakyThrows
    public List<Bucket> listBuckets() {
        return minioClient.listBuckets();
    }

    /**
     * @description: 删除存储桶
     * @param: bucketName
     * @return: boolean
     * @author bfsz
     * @date: 2021/4/19 12:32
     */
    @SneakyThrows
    public boolean removeBucket(String bucketName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable<Result<Item>> myObjects = listObjects(bucketName);
            for (Result<Item> result : myObjects) {
                Item item = result.get();
                // 有对象文件,则删除失败
                if (item.size() > 0) {
                    return false;
                }
            }
            // 删除存储桶,注意,只有存储桶为空时才能删除成功。
            minioClient.removeBucket(bucketName);
            flag = bucketExists(bucketName);
            if (!flag) {
                return true;
            }
        }
        return false;
    }

    /**
     * @description: 列出存储桶中的所有对象名称
     * @param: bucketName
     * @return: java.util.List<java.lang.String>
     * @author bfsz
     * @date: 2021/4/19 12:34
     */
    @SneakyThrows
    public List<String> listObjectNames(String bucketName) {
        List<String> listObjectNames = new ArrayList<>();
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable<Result<Item>> myObjects = listObjects(bucketName);
            for (Result<Item> result : myObjects) {
                Item item = result.get();
                listObjectNames.add(item.objectName());
            }
        }
        return listObjectNames;
    }

    /**
     * @description: 列出存储桶中的所有对象
     * @param: bucketName
     * @return: java.lang.Iterable<io.minio.Result < io.minio.messages.Item>>
     * @author bfsz
     * @date: 2021/4/19 12:35
     */
    @SneakyThrows
    public Iterable<Result<Item>> listObjects(String bucketName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            return minioClient.listObjects(bucketName);
        }
        return null;
    }

    /**
     * @description: 通过文件上传到对象
     * @param: bucketName, objectName, fileName
     * @return: boolean
     * @author bfsz
     * @date: 2021/4/19 12:35
     */
    @SneakyThrows
    public boolean putObject(String bucketName, String objectName, String fileName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            minioClient.putObject(bucketName, objectName, fileName, null);
            ObjectStat statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.length() > 0) {
                return true;
            }
        }
        return false;

    }

    /**
     * @description: 文件上传
     * @param: bucketName: 桶名
     * ------- multipartFile:文件
     * ------- userId: 用户id
     * ------- tableName: 表名
     * ------- tablePrimaryKeyId:表主键id
     * ------- isCompression:是否压缩
     * @return: void
     * @author bfsz
     * @date: 2021/4/19 15:44
     */
    @SneakyThrows
    public JSONObject uploadFile(String bucketName, MultipartFile multipartFile, String tableName, Integer tablePrimaryKeyId, Integer userId, Boolean isCompression) {
        JSONObject res = new JSONObject();
        res.put("code", 500);
        // 判断上传文件是否为空
        if (null == multipartFile || 0 == multipartFile.getSize()) {
            res.put("msg", "上传文件不能为空");
            return res;
        }
        // 判断存储桶是否存在,不存在则新建存储桶
        if (!bucketExists(bucketName)) {
            makeBucket(bucketName);
        }
        // 文件后缀名
        String suffix = Objects.requireNonNull(multipartFile.getOriginalFilename()).substring(multipartFile.getOriginalFilename().lastIndexOf(".") + 1);
        // 1、文件名
        String filename = multipartFile.getOriginalFilename();
        // 2、上传时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        // 3、用户ID
        String UID = "UID" + userId + "_";
        // 4、随机字符串
        String fileString = RandomUtil.randomString(7);
        // 文件存储的目录结构(文件名)
        String pathFileName = tableName + "/" + tablePrimaryKeyId + "/" + sdf.format(new Date()) + "/" + UID + fileString + filename;
        /**
         * 判断是否是图片
         * 判断是否超过了 500K
         */
        if (isCompression && isPicture(suffix) && (1024 * 1024 * 0.5) <= multipartFile.getSize()) {
            // 在项目根目录下的 upload 目录中生成临时文件
            File newFile = new File(Thread.currentThread().getContextClassLoader().getResource("").getPath() + "upload" + UID + "." + suffix);
            // 小于 1M 的
            if ((1024 * 1024 * 0.1) <= multipartFile.getSize() && multipartFile.getSize() <= (1024 * 1024)) {
                Thumbnails.of(multipartFile.getInputStream()).scale(1f).outputQuality(0.8f).toFile(newFile);
            }
            // 1 - 2M 的
            else if ((1024 * 1024) < multipartFile.getSize() && multipartFile.getSize() <= (1024 * 1024 * 2)) {
                Thumbnails.of(multipartFile.getInputStream()).scale(1f).outputQuality(0.6f).toFile(newFile);
            }
            // 2M 以上的
            else if ((1024 * 1024 * 2) < multipartFile.getSize()) {
                Thumbnails.of(multipartFile.getInputStream()).scale(1f).outputQuality(0.4f).toFile(newFile);
            }
            // 获取输入流
            FileInputStream input = new FileInputStream(newFile);
            // 转为 MultipartFile
            MultipartFile multipartFile1 = new MockMultipartFile("file", newFile.getName(), "image/*", input);
            // 开始上传
            PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile1.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
            putObjectOptions.setContentType(multipartFile1.getContentType());
            minioClient.putObject(bucketName, pathFileName, multipartFile1.getInputStream(), putObjectOptions);
            // 删除临时文件
            newFile.delete();
            // 返回状态以及图片路径
            res.put("code", 200);
            res.put("msg", "上传成功");
            res.put("url", getObjectUrl(bucketName, pathFileName));
        }
        // 不需压缩,直接上传
        else {
            // 开始上传
            PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
            putObjectOptions.setContentType(multipartFile.getContentType());
            minioClient.putObject(bucketName, pathFileName, multipartFile.getInputStream(), putObjectOptions);
            // 返回状态以及图片路径
            res.put("code", 200);
            res.put("msg", "上传成功");
            res.put("url", getObjectUrl(bucketName, pathFileName));
        }
        return res;
    }

    /**
     * @description: 通过InputStream上传对象
     * @param: bucketName, objectName, stream
     * @return: boolean
     * @author bfsz
     * @date: 2021/4/19 12:39
     */
    @SneakyThrows
    public boolean putObject(String bucketName, String objectName, InputStream stream) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            minioClient.putObject(bucketName, objectName, stream, new PutObjectOptions(stream.available(), -1));
            ObjectStat statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.length() > 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * @description: 上传文件返回地址
     * @param: bucketName, objectName, stream
     * @return: java.lang.String
     * @author bfsz
     * @date: 2021/4/19 12:40
     */
    @SneakyThrows
    public String putObjectUrl(String bucketName, String objectName, InputStream stream) {
        boolean flag = putObject(bucketName, objectName, stream);
        if (!flag) {
            return "";
        }
        return getObjectUrl(bucketName, objectName);
    }

    /**
     * @description: 以流的形式获取一个文件对象
     * @param: bucketName, objectName
     * @return: java.io.InputStream
     * @author bfsz
     * @date: 2021/4/19 12:41
     */
    @SneakyThrows
    public InputStream getObject(String bucketName, String objectName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            ObjectStat statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.length() > 0) {
                InputStream stream = minioClient.getObject(bucketName, objectName);
                return stream;
            }
        }
        return null;
    }

    /**
     * @description: 以流的形式获取一个文件对象(断点下载)
     * @param: bucketName
     * objectName
     * offset
     * length
     * @return: java.io.InputStream
     * @author bfsz
     * @date: 2021/4/19 12:42
     */
    @SneakyThrows
    public InputStream getObject(String bucketName, String objectName, long offset, Long length) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            ObjectStat statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.length() > 0) {
                InputStream stream = minioClient.getObject(bucketName, objectName, offset, length);
                return stream;
            }
        }
        return null;
    }

    /**
     * @description: 下载并将文件保存到本地
     * @param: bucketName
     * objectName
     * fileName
     * @return: boolean
     * @author bfsz
     * @date: 2021/4/19 12:43
     */
    @SneakyThrows
    public boolean getObject(String bucketName, String objectName, String fileName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            ObjectStat statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.length() > 0) {
                minioClient.getObject(bucketName, objectName, fileName);
                return true;
            }
        }
        return false;
    }

    /**
     * @description: 删除一个对象
     * @param: bucketName
     * objectName
     * @return: boolean
     * @author bfsz
     * @date: 2021/4/19 12:43
     */
    @SneakyThrows
    public JSONObject deleteFile(String bucketName, String objectName) {
        JSONObject res = new JSONObject();
        boolean flag = bucketExists(bucketName);
        if (flag) {
            minioClient.removeObject(bucketName, objectName);
            res.put("code", "0");
            res.put("msg", "success");
            return res;
        }
        res.put("code", "1");
        res.put("msg", "error");
        return res;
    }

    /**
     * @description: 删除指定桶的多个文件对象, 返回删除错误的对象列表,全部删除成功,返回空列表
     * @param: bucketName
     * objectNames
     * @return: java.util.List<java.lang.String>
     * @author bfsz
     * @date: 2021/4/19 12:43
     */
    @SneakyThrows
    public List<String> removeObject(String bucketName, List<String> objectNames) {
        List<String> deleteErrorNames = new ArrayList<>();
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable<Result<DeleteError>> results = minioClient.removeObjects(bucketName, objectNames);
            for (Result<DeleteError> result : results) {
                DeleteError error = result.get();
                deleteErrorNames.add(error.objectName());
            }
        }
        return deleteErrorNames;
    }

    /**
     * @description: 生成一个给HTTP GET请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
     * @param: bucketName
     * objectName
     * expires
     * @return: java.lang.String
     * @author bfsz
     * @date: 2021/4/19 12:44
     */
    @SneakyThrows
    public String presignedGetObject(String bucketName, String objectName, Integer expires) {
        boolean flag = bucketExists(bucketName);
        String url = "";
        if (flag) {
            if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
                throw new InvalidExpiresRangeException(expires,
                        "过期时间: 1 - " + DEFAULT_EXPIRY_TIME);
            }
            url = minioClient.presignedGetObject(bucketName, objectName, expires);
        }
        return url;
    }

    /**
     * @description: 生成一个给HTTP PUT请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
     * @param: bucketName
     * objectName
     * expires
     * @return: java.lang.String
     * @author bfsz
     * @date: 2021/4/19 12:47
     */
    @SneakyThrows
    public String presignedPutObject(String bucketName, String objectName, Integer expires) {
        boolean flag = bucketExists(bucketName);
        String url = "";
        if (flag) {
            if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
                throw new InvalidExpiresRangeException(expires,
                        "expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);
            }
            url = minioClient.presignedPutObject(bucketName, objectName, expires);
        }
        return url;
    }

    /**
     * @description: 获取对象的元数据
     * @param: bucketName
     * objectName
     * @return: io.minio.ObjectStat
     * @author bfsz
     * @date: 2021/4/19 12:47
     */
    @SneakyThrows
    public ObjectStat statObject(String bucketName, String objectName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            ObjectStat statObject = minioClient.statObject(bucketName, objectName);
            return statObject;
        }
        return null;
    }

    /**
     * @description: 文件访问路径
     * @param: bucketName
     * objectName
     * @return: java.lang.String
     * @author bfsz
     * @date: 2021/4/19 12:47
     */
    @SneakyThrows
    public String getObjectUrl(String bucketName, String objectName) {
        boolean flag = bucketExists(bucketName);
        String url = "";
        if (flag) {
            url = minioClient.getObjectUrl(bucketName, objectName);
        }
        return url;
    }

    /**
     * @description: 下载文件
     * @param: bucketName
     * fileName
     * originalName
     * response
     * @return: void
     * @author bfsz
     * @date: 2021/4/19 12:47
     */
    public void downloadFile(String bucketName, String fileName, String originalName, HttpServletResponse response) {
        try {
            InputStream file = minioClient.getObject(bucketName, fileName);
            String filename = new String(originalName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            ServletOutputStream servletOutputStream = response.getOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = file.read(buffer)) > 0) {
                servletOutputStream.write(buffer, 0, len);
            }
            servletOutputStream.flush();
            file.close();
            servletOutputStream.close();
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Controller测试

package com.zyy.controller;

import com.zyy.common.utils.minio.MinioUtil;
import com.zyy.repository.mapper.TbSysUserMapper;
import com.zyy.repository.entity.TbSysUser;
import net.minidev.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;


@Api(tags = "测试连接相关")
@RestController
@RequestMapping("/admin")
public class TestConnectController {
    @Autowired
    private TbSysUserMapper tbSysUserMapper;
    private static final Logger LOGGER = LoggerFactory.getLogger(TestConnectController.class);

    /**
     * @description:文件上传测试
     * @param: file
     * projectId
     * user_id
     * @return: net.minidev.json.JSONObject
     * @author bfsz
     * @date: 2021/4/20 10:42
     */
    @ApiOperation("文件上传测试接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文件", defaultValue = "", required = true),
            @ApiImplicitParam(name = "projectId", value = "任务ID", defaultValue = "", required = true),
            @ApiImplicitParam(name = "user_id", value = "用户ID", defaultValue = "", required = true)
    })
    @RequestMapping(value = "/upload2", method = RequestMethod.POST)
    public JSONObject uploadDemo(MultipartFile file, Integer projectId, Integer user_id) {
        MinioUtil minioUtil = new MinioUtil();
        return minioUtil.putObject("wypt", file, projectId, user_id, true);
    }

    @ApiOperation("文件删除测试接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "bucketName", value = "存储桶名称", defaultValue = "", required = true),
            @ApiImplicitParam(name = "objectName", value = "文件名称", defaultValue = "", required = true)
    })
    @RequestMapping(value = "/delete2", method = RequestMethod.POST)
    public JSONObject deleteDemo(String bucketName, String objectName) {
        MinioUtil minioUtil = new MinioUtil();
        return minioUtil.removeObject(bucketName, objectName);
    }
}

thumbnailator 图片压缩上传至MinIO服务器

  • Thumbnailator 是一个用来生成图像缩略图的 Java 类库,通过很简单的代码即可生成图片缩略图,也可直接对一整个目录的图片生成缩略图
  • 支持图片缩放,区域裁剪,水印,旋转,保持比例
  • https://blog.csdn.net/s12s12c12/article/details/106127596
  • https://lizhou.blog.csdn.net/article/details/108321442

引入 maven 依赖

<dependency>
    <groupId>net.coobird</groupId>
    <artifactId>thumbnailator</artifactId>
    <version>0.4.14</version>
</dependency>

增加文件类型判断工具类

package com.zyy.common.utils.thumbnailator;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * @author bfsz
 * @version 1.0
 * @description: 文件类型判断
 * @date 2021/4/19 16:21
 */
public class FileSuffixUtils {
    /**
     * @param file
     * @description: 判断文件是否为图片
     * @param: imgName
     * @return: boolean
     * @author bfsz
     * @date: 2021/4/19 16:22
     */
    public static boolean isPicture(MultipartFile file) throws IOException {
        BufferedImage bi = ImageIO.read(file.getInputStream());
        if (bi == null) {
            return false;
        }
        return true;
    }

    /**
     * @description: 判断文件是否为图片
     * @param: imgName
     * @return: boolean
     * @author bfsz
     * @date: 2021/4/20 8:36
     */
    public static boolean isPicture(String imgName) {
        boolean flag = false;
        if (StringUtils.isBlank(imgName)) {
            return false;
        }
        String[] arr = {"bmp", "dib", "gif", "jfif", "jpe", "jpeg", "jpg", "png", "tif", "tiff", "ico"};
        for (String item : arr) {
            if (item.equals(imgName)) {
                flag = true;
                break;
            }
        }
        return flag;
    }
}

压缩上传

/**
     * @description: 文件上传
     * @param: bucketName: 桶名
     * ------- multipartFile:文件
     * ------- userId: 用户id
     * ------- projectId:任务id
     * ------- isCompression:是否压缩
     * @return: void
     * @author bfsz
     * @date: 2021/4/19 15:44
     */
    @SneakyThrows
    public JSONObject putObject(String bucketName, MultipartFile multipartFile, Integer projectId, Integer userId, Boolean isCompression) {
        JSONObject res = new JSONObject();
        res.put("code", 500);
        // 判断上传文件是否为空
        if (null == multipartFile || 0 == multipartFile.getSize()) {
            res.put("msg", "上传文件不能为空");
            return res;
        }
        // 判断存储桶是否存在,不存在则新建存储桶
        if (!bucketExists(bucketName)) {
            makeBucket(bucketName);
        }
        // 文件后缀名
        String suffix = Objects.requireNonNull(multipartFile.getOriginalFilename()).substring(multipartFile.getOriginalFilename().lastIndexOf(".") + 1);
        // 文件名
        String filename = multipartFile.getOriginalFilename();
        // 上传时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        // 用户ID
        String UID = "UID_" + userId + "_";
        // 文件存储的目录结构(文件名)
        String pathFileName = projectId + "/" + sdf.format(new Date()) + "/" + UID + filename;
        /**
         * 判断是否是图片
         * 判断是否超过了 500K
         */
        if (isCompression && isPicture(suffix) && (1024 * 1024 * 0.5) <= multipartFile.getSize()) {
            // 在项目根目录下的 upload 目录中生成临时文件
            File newFile = new File(Thread.currentThread().getContextClassLoader().getResource("").getPath() + "upload" + UID + "." + suffix);
            // 小于 1M 的
            if ((1024 * 1024 * 0.1) <= multipartFile.getSize() && multipartFile.getSize() <= (1024 * 1024)) {
                Thumbnails.of(multipartFile.getInputStream()).scale(1f).outputQuality(0.8f).toFile(newFile);
            }
            // 1 - 2M 的
            else if ((1024 * 1024) < multipartFile.getSize() && multipartFile.getSize() <= (1024 * 1024 * 2)) {
                Thumbnails.of(multipartFile.getInputStream()).scale(1f).outputQuality(0.6f).toFile(newFile);
            }
            // 2M 以上的
            else if ((1024 * 1024 * 2) < multipartFile.getSize()) {
                Thumbnails.of(multipartFile.getInputStream()).scale(1f).outputQuality(0.4f).toFile(newFile);
            }
            // 获取输入流
            FileInputStream input = new FileInputStream(newFile);
            // 转为 MultipartFile
            MultipartFile multipartFile1 = new MockMultipartFile("file", newFile.getName(), "image/*", input);
            // 开始上传
            PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile1.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
            putObjectOptions.setContentType(multipartFile1.getContentType());
            minioClient.putObject(bucketName, pathFileName, multipartFile1.getInputStream(), putObjectOptions);
            // 删除临时文件
            newFile.delete();
            // 返回状态以及图片路径
            res.put("code", 200);
            res.put("msg", "上传成功");
            res.put("url", getObjectUrl(bucketName, pathFileName));
        }
        // 不需压缩,直接上传
        else {
            // 开始上传
            PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
            putObjectOptions.setContentType(multipartFile.getContentType());
            minioClient.putObject(bucketName, pathFileName, multipartFile.getInputStream(), putObjectOptions);
            // 返回状态以及图片路径
            res.put("code", 200);
            res.put("msg", "上传成功");
            res.put("url", getObjectUrl(bucketName, pathFileName));
        }
        return res;
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
校园文件传输说明文档 版本1.0.20100905 使用方法: 首先接受方和发送方必须正常运行本程序,发送方在 接收方地址 栏 填入接收方的IP(也可以是IPv6)地址,打开要发送的文件,点击发送,等待对方回应即可 1.本软件同时支持IPv4和IPv6协议,只要双方主机有一种协议可接入网络即可 2.双击处于Running 状态的任务,可以停止任务,注意本软件暂不支持断点续传,停止后此次任务将失败 3.支持文件鼠标拖拽 4.暂不支持文件夹传送 常见问题(Q&A) 1.传输范围 理论上两台主机,只要发送方能ping通接收方,发送方就可以给接收方发送文件, 局域网内可以互传; 全校内可以互传; 不同学校可以互传; ADSL宽带用户可以传; 注意:如果双方在不同的内网下是无法传输的,记住只要发送方能ping通接收方,发送方就可以给接收方发送文件 2.和飞鸽的区别 飞鸽只是局域网传输,不同局域网是无法传输的,本软件无此限制;关于用户列表问题,只有局域网才可以获得用户列表,所以飞鸽有,本程序没有,因为超脱局域网是无法确定用户的 3.和QQ的区别 首先说明QQ的传输能力很强,本软件很多方面暂时还比不上,但某些方面还是比QQ有优势,如下 a.QQ需要你有账户,并且要登录成功,还只能好友间才能传输,本软件无此限制 b.QQ不支持IPv6协议,对于校园网络用户来说每个月的流量可是很宝贵的,而QQ会占用IPv4流量,致使根本不敢过多传输大文件,而一般高校是不限制IPv6流量的,而且IPv6速度也较快,又不限制流量,何乐而不为呢。事实上本软件的作者就是因为这个原因才开发此软件的。经历多年呕心沥血不吃不喝不眠不休,翻阅n多资料,敲烂n多键盘,终于大功告成。
可以使用Minio Java客户端来实现Spring BootMinio集成,下面是一个基本的步骤: 1.引入Minio Java客户端的依赖: ``` <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>7.0.2</version> </dependency> ``` 2.配置Minio的连接信息: ``` @Configuration public class MinioConfig { @Value("${minio.url}") private String url; @Value("${minio.accessKey}") private String accessKey; @Value("${minio.secretKey}") private String secretKey; @Bean public MinioClient minioClient() throws Exception { return new MinioClient(url, accessKey, secretKey); } } ``` 其中minio.url是Minio服务器的地址,minio.accessKey和minio.secretKey是访问Minio服务器所需的密钥信息。 3.编写文件上传接口: ``` @RestController public class FileUploadController { @Autowired private MinioClient minioClient; @PostMapping("/upload") // 访问路径 public String upload(@RequestParam("file") MultipartFile file) { try { String fileName = file.getOriginalFilename(); InputStream inputStream = file.getInputStream(); minioClient.putObject("bucketName", fileName, inputStream, file.getContentType()); return "上传成功"; } catch (Exception e) { e.printStackTrace(); return "上传失败"; } } } ``` 其中minioClient.putObject()方法的参数解释如下: - bucketName:存储桶的名称,如果不存在则会自动创建 - fileName:文件在存储桶中的名称 - inputStream:文件的输入流 - contentType:文件类型,如text/plain、image/jpeg等 4.测试上传接口: 启动Spring Boot应用,并使用Postman等工具测试上传接口,文件上传成功后会在Minio服务器中创建一个存储桶,并且文件会存储在其中。 以上就是Spring Boot集成Minio实现文件批量上传的过程,希望对你有帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值