springboot使用minio

61 阅读5分钟

minio项目结构 image.png

pom引用

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
        <version>${minio.version}</version>
    </dependency>
    <!-- io常用工具类 -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>${commons.io.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>${hutool.version}</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

项目配置MinIOService

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.cp.demo.constant.ResultVO;
import com.cp.demo.constant.ResultVOUtil;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.rowset.serial.SerialException;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author chenp
 * @date 2023/9/15
 */
@Data
@Component
@Slf4j
public class MinIOService {
    @Value("${minio.address}")
    private String address;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;
    @Value("${minio.bucketName}")
    private String bucketName;

    public MinioClient getMinioClient() {
        try {
            return  MinioClient.builder()
                    .endpoint(address)
                    .credentials(accessKey,secretKey)
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 检查存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return
     */
    public boolean bucketExists(MinioClient minioClient, String bucketName) {
        boolean flag = false;
        try {
            flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (flag) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    // Upload '/home/user/Photos/asiaphotos.zip' as object name 'asiaphotos-2015.zip' to bucket
    // 'asiatrip'.


    /**
     * 创建存储桶
     *
     * @param bucketName 存储桶名称
     */
    public boolean makeBucket(MinioClient minioClient, String bucketName) {
        try {
            boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            //存储桶不存在则创建存储桶
            if (!found) {
                // 如果不存在创建桶
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 上传文件
     *
     * @param file 上传文件
     * @return 成功则返回文件名,失败返回空
     */
    public ResultVO uploadFile(MinioClient minioClient, MultipartFile file) throws SerialException {
        //创建存储桶
        boolean createFlag = makeBucket(minioClient, bucketName);
        //创建存储桶失败
        if (createFlag == false) {
            throw new SerialException("创建存储桶失败");
        }
        try {
            String originalFilename = file.getOriginalFilename();
            int pointIndex = originalFilename.lastIndexOf(".");
            //得到文件流
            InputStream inputStream = file.getInputStream();
            //保证文件不重名(并且没有特殊字符)
            String fileName = bucketName+ DateUtil.format(new Date(), "_yyyy-MM-dd-HH-mm-ss") + (pointIndex > -1 ? originalFilename.substring(pointIndex) : "");
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).contentType(ViewContentType.getContentType(fileName)).stream(inputStream,inputStream.available(),-1).build());

            inputStream.close();
            return ResultVOUtil.success(fileName);
        } catch (Exception e) {
            e.printStackTrace();
            return ResultVOUtil.error();
        }
    }

    /**
     * 下载文件
     *
     * @param originalName 文件路径*/

    public InputStream downloadFile(MinioClient minioClient, String originalName, HttpServletResponse response) {
        try {
            // 2. 获取对象的InputStream,并保存为文件
            InputStream file = minioClient.getObject(
                    GetObjectArgs.builder().bucket(bucketName).object(originalName).build());
            String filename = new String(originalName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
            if (StrUtil.isNotBlank(originalName)) {
                filename = originalName;
            }
            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();
            return file;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 删除文件
     *
     * @param fileName 文件路径
     * @return
     */
    public boolean deleteFile(MinioClient minioClient, String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * Description 生成预览链接,最大7天有效期;
     * 如果想永久有效,在 minio 控制台设置仓库访问规则总几率
     * Create by Mr.Fang
     *
     * @param object      文件名称
     * @param contentType 预览类型 image/gif", "image/jpeg", "image/jpg", "image/png", "application/pdf
     * @Params
     **/
    /**
     * @description:获取文件预览接口
     * @date: 2022/8/18 9:44
     * @param: fileName: 文件名
     * @Param: bucketName: 桶名
     * @Param: previewExpiry: 预览到期时间(小时)
     * @return: java.lang.String
     **/
    public String getPreviewUrl(MinioClient minioClient,String fileName,Integer previewExpiry) {
        if (StringUtils.isNotBlank(fileName)) {
            bucketName = StringUtils.isNotBlank(bucketName) ? bucketName : this.bucketName;
            try {
                minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
                if (null != previewExpiry){
                    return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(fileName)
                            .expiry(previewExpiry, TimeUnit.HOURS)
                            .build());
                }else {
                    return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(fileName)
                            .build());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    /**
     * 查询所有桶文件
     *
     * @return
     */
    public  List<Bucket> getListBuckets(MinioClient minioClient) {
        try {
            return minioClient.listBuckets();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Collections.emptyList();
    }
    /**
     * 批量删除文件
     * @param objects 对象名称
     * @return boolean
     */
    public List<DeleteError> deleteObject(MinioClient minioClient,List<String> objects) {
        List<DeleteError> deleteErrors = new ArrayList<>();
        List<DeleteObject> deleteObjects = objects.stream().map(value -> new DeleteObject(value)).collect(Collectors.toList());
        Iterable<Result<DeleteError>> results =
                minioClient.removeObjects(
                        RemoveObjectsArgs
                                .builder()
                                .bucket(bucketName)
                                .objects(deleteObjects)
                                .build());
        try {
            for (Result<DeleteError> result : results) {
                DeleteError error = result.get();
                deleteErrors.add(error);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return deleteErrors;
    }
    /**
     * 获取单个桶中的所有文件对象名称
     *
     * @param bucket 桶名称
     * @return {@link List}<{@link String}>
     */
    public  List<String> getBucketObjectName(MinioClient minioClient,String bucket) {
        boolean exsit = makeBucket(minioClient,bucket);
        if (exsit) {
            List<String> listObjetcName = new ArrayList<>();
            try {
                Iterable<Result<Item>> myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).build());
                for (Result<Item> result : myObjects) {
                    Item item = result.get();
                    listObjetcName.add(item.objectName());
                }
                return listObjetcName;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    /**
     * 获取某个桶下某个对象的URL
     *
     * @param objectName 对象名 (文件夹名 )
     * @return
     */
    public  String getBucketObject(MinioClient minioClient, String objectName) throws Exception {
        // 删除之前先检查`my-bucket`是否存在。
        boolean found = makeBucket(minioClient,bucketName);
        if (found) {
            final GetObjectResponse object = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
            return object.object();
        }
        return "";
    }
    /**
     * @description: 批量下载
     * @param: filenames: 多个文件名称
     * @Param: zip: 压缩包名称
     * @Param: res: 响应对象
     * @return: void
     **/
    public void batchDownload(MinioClient minioClient, List<String> filenames, String zip, HttpServletResponse res, HttpServletRequest req){
        try {
            BucketExistsArgs bucketArgs = BucketExistsArgs.builder().bucket(bucketName).build();
            boolean bucketExists = minioClient.bucketExists(bucketArgs);
            BufferedOutputStream bos = null;
            res.reset();
            bos = new BufferedOutputStream(res.getOutputStream());
            ZipOutputStream out = new ZipOutputStream(bos);
            res.setHeader("Access-Control-Allow-Origin", "*");
            for (int i=0;i<filenames.size();i++) {
                GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
                        .object(filenames.get(i)).build();
                InputStream object = minioClient.getObject(objectArgs);
                byte buf[] = new byte[1024];
                int length = 0;
                res.setCharacterEncoding("utf-8");
                res.setContentType("application/force-download");// 设置强制下载不打开
                res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
                res.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoding(zip,req) + ".zip");
                out.putNextEntry(new ZipEntry(filenames.get(i)));
                while ((length = object.read(buf)) > 0) {
                    out.write(buf, 0, length);
                }
            }
            out.close();
            bos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *  设置不同浏览器编码
     * @param filename 文件名称
     * @param request 请求对象
     */
    public static String filenameEncoding(String filename, HttpServletRequest request) throws UnsupportedEncodingException {
        // 获得请求头中的User-Agent
        String agent = request.getHeader("User-Agent");
        // 根据不同的客户端进行不同的编码

        if (agent.contains("MSIE")) {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        } else if (agent.contains("Firefox")) {
            // 火狐浏览器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
        } else {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }
}

ViewContentType

import cn.hutool.core.util.StrUtil;

/**
 * @author chenp
 * @date 2023/9/15
 */

public enum ViewContentType {
    DEFAULT("default","application/octet-stream"),
    JPG("jpg", "image/jpeg"),
    TIFF("tiff", "image/tiff"),
    GIF("gif", "image/gif"),
    JFIF("jfif", "image/jpeg"),
    PNG("png", "image/png"),
    TIF("tif", "image/tiff"),
    ICO("ico", "image/x-icon"),
    JPEG("jpeg", "image/jpeg"),
    WBMP("wbmp", "image/vnd.wap.wbmp"),
    FAX("fax", "image/fax"),
    NET("net", "image/pnetvue"),
    JPE("jpe", "image/jpeg"),
    RP("rp", "image/vnd.rn-realpix");

    private String prefix;

    private String type;

    public static String getContentType(String prefix){
        if(StrUtil.isEmpty(prefix)){
            return DEFAULT.getType();
        }
        prefix = prefix.substring(prefix.lastIndexOf(".") + 1);
        for (ViewContentType value : ViewContentType.values()) {
            if(prefix.equalsIgnoreCase(value.getPrefix())){
                return value.getType();
            }
        }
        return DEFAULT.getType();
    }

    ViewContentType(String prefix, String type) {
        this.prefix = prefix;
        this.type = type;
    }

    public String getPrefix() {
        return prefix;
    }

    public String getType() {
        return type;
    }
}

FileController


import com.cp.demo.config.MinIOService;
import com.cp.demo.constant.ResultVO;
import com.cp.demo.constant.ResultVOUtil;
import io.minio.MinioClient;
import io.minio.messages.DeleteError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.rowset.serial.SerialException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author chenp
 * @date 2023/9/15
 */
@RequestMapping("/file")
@RestController
public class FileController {
    @Autowired
    private MinIOService minIOService;

    /**
     * 上传文件
     *
     * @param file 文件
     * @return
     */
    @PostMapping("/uploadFile")
    public ResultVO uploadFile(@RequestBody MultipartFile file) throws SerialException {
        MinioClient minioClient = minIOService.getMinioClient();
        if (minioClient == null) {
            throw new SerialException("连接MinIO服务器失败");
        }
        return minIOService.uploadFile(minioClient, file);
    }

    /*
     *
     * 下载文件
     *
     * @param response 返回请求
     * @param fileName 文件路径
     * @return
     */

    @GetMapping("/downloadFile")
    public String downloadFile(HttpServletResponse response,@RequestParam String fileName) throws SerialException {
        MinioClient minioClient = minIOService.getMinioClient();
        if (minioClient == null) {
            throw new SerialException("连接MinIO服务器失败");
        }
        return minIOService.downloadFile(minioClient, fileName, response) != null ? "下载成功" : "下载失败";
    }

    /**
     * 删除文件
     *
     * @param fileName 文件路径
     * @return
     */
    @DeleteMapping("/deleteFile/{fileName}")
    public ResultVO deleteFile(@PathVariable("fileName") String fileName) throws SerialException {
        MinioClient minioClient = minIOService.getMinioClient();
        if (minioClient == null) {
            throw new SerialException("连接MinIO服务器失败");
        }
        boolean flag = minIOService.deleteFile(minioClient, fileName);
        return flag == Boolean.TRUE ? ResultVOUtil.success("删除成功") : ResultVOUtil.success("删除失败");
    }
    /**
     * 文件预览*/
    @GetMapping("/prevpreviewFile/{fileName}")
    public ResultVO previewFile(@PathVariable(value = "fileName",required = true) String fileName) throws SerialException {
        MinioClient minioClient = minIOService.getMinioClient();
        if (minioClient == null) {
            throw new SerialException("连接MinIO服务器失败");
        }
        Integer previewExpiry=1;
        String previewUrl = minIOService.getPreviewUrl(minioClient, fileName, previewExpiry);
        return ResultVOUtil.success(previewUrl);
    }
    /**
     * 获取某个桶下某个对象的URL
     * */
    @GetMapping("/getBucketObject/{fileName}")
    public ResultVO getBucketObject(@PathVariable(value = "fileName",required = true) String fileName) throws Exception {
        MinioClient minioClient = minIOService.getMinioClient();
        if (minioClient == null) {
            throw new SerialException("连接MinIO服务器失败");
        }
        return ResultVOUtil.success( minIOService.getBucketObject(minioClient, fileName));
    }
    /**
     * 批量删除某个文件
     * */
    @PostMapping("/getRemoveObjects")
    public ResultVO getRemoveObjects(@RequestBody ArrayList<String> objectNames) throws SerialException {
        MinioClient minioClient = minIOService.getMinioClient();
        if (minioClient == null) {
            throw new SerialException("连接MinIO服务器失败");
        }
        List<DeleteError> deleteErrors = minIOService.deleteObject(minioClient, objectNames);
        return ResultVOUtil.success(deleteErrors);
    }
    /*多文件上传**/
    @PostMapping("/uploadAllFile")
    public ResultVO upload(@RequestBody MultipartFile[] files) throws Exception {
        MinioClient minioClient = minIOService.getMinioClient();
        if (minioClient == null) {
            throw new SerialException("连接MinIO服务器失败");
        }
        for (MultipartFile file : files) {
            minIOService.uploadFile(minioClient, file);
        }
        return ResultVOUtil.success();
    }
    @PostMapping("/batchDownload")
    public void batchDownload(@RequestBody() List<String> fileNames,HttpServletResponse res, HttpServletRequest req) throws SerialException {
        MinioClient minioClient = minIOService.getMinioClient();
        if (minioClient == null) {
            throw new SerialException("连接MinIO服务器失败");
        }
        minIOService.batchDownload(minioClient,fileNames,"1.zip",res,req);
    }
}

返回类 ResultVOUtil ResultVO


/**
 * @author chenp
 * @date 2023/09/01
 */
public class ResultVO {
    private static final long serialVersionUID = 1L;

    private Object data;//信息
    private Object code;//错误码
    private Object msg;//错误提示

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Object getCode() {
        return code;
    }

    public void setCode(Object code) {
        this.code = code;
    }

    public Object getMsg() {
        return msg;
    }

    public void setMsg(Object msg) {
        this.msg = msg;
    }
}

/**
 * @Description: 返回结果封装类
 * @UpdateRemark: 修改内容
 */
public class ResultVOUtil {
    private ResultVOUtil() {

    }

    private static final String success = "success";
    private static final String code = "0";

    //不带返回数据
    public static ResultVO success() {
        ResultVO result = new ResultVO();
        result.setMsg(success);
        result.setCode(code);
        return result;
    }

    //带返回数据
    public static ResultVO success(Object Data) {
        ResultVO result = new ResultVO();
        result.setData(Data);
        result.setCode(code);
        result.setMsg(success);
        return result;
    }

    public static ResultVO success(Integer code, Object data, String message) {
        ResultVO result = new ResultVO();
        result.setCode(code);
        result.setData(data);
        result.setMsg(success);
        return result;
    }

    public static ResultVO error() {
        ResultVO result = new ResultVO();
        result.setCode("-1");
        result.setMsg("fail");
        return result;
    }


    public static ResultVO error(Integer code, String message) {
        ResultVO result = new ResultVO();
        result.setCode(code);
        result.setMsg(message);
        return result;
    }
    public static ResultVO error(Object data) {
        ResultVO result = new ResultVO();
        result.setCode(-1);
        result.setMsg("");
        result.setData(data);
        return result;
    }

    public static ResultVO errorDate(Object data, String msg) {
        ResultVO result = new ResultVO();
        result.setData(data);
        result.setMsg(msg);
        result.setCode(-1);
        return result;
    }

  /*  public static ResultVO error(ResultCodesEnum resultCode) {
        ResultVO result = new ResultVO();
        result.setCode(resultCode.getCode());
        result.setMsg(resultCode.getMsg());
        return result;
    }*/

}