All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.fscode.commons.minio.utils.MinioUtils Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
package cn.fscode.commons.minio.utils;

import cn.fscode.commons.minio.client.CustomMinioClient;
import cn.fscode.commons.storage.api.config.MinioStorageProperties;
import cn.fscode.commons.storage.api.partupload.MultiPartUploadInit;
import cn.fscode.commons.tool.core.UrlUtils;
import cn.fscode.commons.tool.core.exception.UtilException;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import io.minio.messages.Part;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * minio工具类
 * 对象名: xxx/zzz/yyy/fileName.后缀 ==> xxx/zzz/yyy是目录名
 *
 * @author shenguangyang
 */
public class MinioUtils {
    /**
     * logger
     */
    private final static Logger logger = LoggerFactory.getLogger(MinioUtils.class);

    /**
     * url过期时间
     */
    private static MinioStorageProperties minioProperties;

    /**
     * 如果为 application/octet-stream 获取的图片链接只可以下载的
     * image/png:表示可以在线预览
     */
    private static final String CONTENT_TYPE = "application/octet-stream";
    /**
     * minioClient
     */
    private static CustomMinioClient minioClient;

    public static void init(CustomMinioClient customMinioClient, MinioStorageProperties minioProperties) {
        MinioUtils.minioClient = customMinioClient;
        MinioUtils.minioProperties = minioProperties;
    }

    /**
     * 创建桶
     *
     * @param bucketName 桶的名字
     * @throws Exception
     */
    public static void createBucket(String bucketName) throws Exception {
        if (bucketExists(bucketName)) {
            return;
        }
        minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 批量删除对象
     *
     * @param bucketName  桶名
     * @param objectNames 对象名
     */
    public static void deleteObjects(String bucketName, List objectNames) throws Exception {
        List deletedObjects = new ArrayList<>(objectNames.size());
        for (String objectName : objectNames) {
            deletedObjects.add(new DeleteObject(objectName));
        }
        RemoveObjectsArgs removeObjectArgs = RemoveObjectsArgs.builder()
                .bucket(bucketName).objects(deletedObjects).build();
        for (Result errorResult : minioClient.removeObjects(removeObjectArgs)) {
            DeleteError deleteError = errorResult.get();
            throw new UtilException("delete fail, objectName: " + deleteError.objectName() + ", bucketName: " + bucketName);
        }
    }

    public static void deleteObject(String bucketName, String objectName) throws Exception {
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                .bucket(bucketName).object(objectName).build();
        minioClient.removeObject(removeObjectArgs);
    }

    /**
     * 判断对象是否存在
     *
     * @param bucketName 桶名
     * @param objectName 对象名
     */
    public static boolean checkFileIsExist(String bucketName, String objectName) {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder().bucket(bucketName).object(objectName).build()
            );
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 判断文件夹是否存在
     *
     * @param folderName 文件夹名称, 不能以 / 结尾
     * @return true存在, 反之
     */
    public static List listFilePath(String bucketName, String folderName, Consumer filePathCallback) {
        List out = new ArrayList<>();
        try {
            Iterable> results = minioClient.listObjects(
                    ListObjectsArgs
                            .builder()
                            .bucket(bucketName)
                            .prefix(folderName)
                            .recursive(true)
                            .build());
            for (Result result : results) {
                Item item = result.get();
                if (!item.isDir()) {
                    if (filePathCallback != null) {
                        filePathCallback.accept(item.objectName());
                    }
                    out.add(item.objectName());
                }
            }
        } catch (Exception e) {
            return out;
        }
        return out;
    }

    /**
     * 判断文件夹是否存在
     *
     * @param folderName 文件夹名称, 不能以 / 结尾
     * @return true存在, 反之
     */
    public static Boolean checkFolderIsExist(String bucketName, String folderName) {
        try {
            Iterable> results = minioClient.listObjects(
                    ListObjectsArgs
                            .builder()
                            .bucket(bucketName)
                            .prefix(folderName)
                            .recursive(false)
                            .build());
            for (Result result : results) {
                Item item = result.get();
                if (item.isDir() && folderName.equals(UrlUtils.removeEndSlash(item.objectName()))) {
                    return true;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return false;
    }

    /**
     * 分片上传
     * 需要重写minio客户端,{@link CustomMinioClient}
     *
     * @apiNote 注意一个分片小于5M,会报错,提示
     * Your proposed upload is smaller than the minimum allowed object size.
     */
    public static class MultiPartUpload {
        /**
         * @param bucketName 桶的名字 如果桶的名字为null或者为空字符串,则使用默认的桶
         * @param objectName 对象名
         * @param totalPart  一共分片数
         * @return 返回到的结果有两个字段
         * uploadId:上传的id
         * uploadUrls:每个分片上传url的集合,只能通过put进行上传
         * 响应结果:
         * {
         * "uploadId": "b7dd9a60-7c11-43f1-acee-bffd4ef2fccb",
         * "uploadUrls": [
         * "url1",
         * "url2"
         * ]
         * }
         * eg: minio-multipart-upload
         */
        public static MultiPartUploadInit init(String bucketName, String objectName, int totalPart) {
            MultiPartUploadInit result = new MultiPartUploadInit();
            try {
                String uploadId = minioClient.initMultiPartUpload(bucketName, null, objectName, null, null);

                result.setUploadId(uploadId);

                Map uploadUrls = new HashMap<>(32);

                Map reqParams = new HashMap<>();
                //reqParams.put("response-content-type", "application/json");
                reqParams.put("uploadId", uploadId);
                for (int i = 1; i <= totalPart; i++) {
                    reqParams.put("partNumber", String.valueOf(i));
                    String uploadUrl = minioClient.getPresignedObjectUrl(
                            GetPresignedObjectUrlArgs.builder()
                                    .method(Method.PUT)
                                    .bucket(bucketName)
                                    .object(objectName)
                                    .expiry(1, TimeUnit.DAYS)
                                    .extraQueryParams(reqParams)
                                    .build());
                    uploadUrls.put(String.valueOf(i), uploadUrl);
                }
                result.setUploadUrls(uploadUrls);
            } catch (Exception e) {
                logger.error("error: {}", e.getMessage(), e);
                return result;
            }
            return result;
        }

        /**
         * 合并所有分片
         *
         * @param objectName 对象名
         * @param bucketName 桶的名字
         * @param uploadId   上传文件的eid
         * @return false合并失败,true合并成功
         */
        public static boolean merge(String bucketName, String objectName, String uploadId) {
            try {
                Part[] parts = new Part[1000];
                //此方法注意2020.02.04之前的minio服务端有bug
                ListPartsResponse partResult = minioClient.listMultipart(bucketName, null, objectName, 1000, 0, uploadId, null, null);
                int partNumber = 1;
                for (Part part : partResult.result().partList()) {
                    parts[partNumber - 1] = new Part(partNumber, part.etag());
                    partNumber++;
                }
                minioClient.mergeMultipartUpload(bucketName, null, objectName, uploadId, parts, null, null);
            } catch (Exception e) {
                logger.error("error: {}", e.getMessage(), e);
                return false;
            }
            return true;
        }
    }


    /**
     * 上传文件
     *
     * @param file       文件
     * @param bucketName 桶的名字
     * @param objectName 对象名 比如 xxx/zzz/fileName.jpg
     */
    public static void uploadFile(String bucketName, MultipartFile file, String objectName) {
        try (ByteArrayInputStream bais = new ByteArrayInputStream(file.getBytes())) {
            // 文件名 file.getOriginalFilename();
            uploadFile(bucketName, bais, file.getContentType(), objectName);
        } catch (Exception e) {
            logger.error("{}文件上传失败", file.getOriginalFilename());
            throw new RuntimeException("文件上传失败," + file.getOriginalFilename());
        }
    }

    /**
     * 上传文件
     *
     * @param fileBase64 文件base64
     * @param objectName 对象名
     */
    public static void uploadFile(String bucketName, String fileBase64, String objectName) throws Exception {
        uploadFile(bucketName, fileBase64, CONTENT_TYPE, objectName);
    }

    /**
     * 判断桶是否存在
     *
     * @param bucketName 桶名
     * @return 是否存在
     */
    public static boolean bucketExists(String bucketName) {
        // 判断Bucket是否存在
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            logger.error(e.getMessage());
            return false;
        }
    }

    /**
     * 上传文件
     *
     * @param fileBase64  文件base64
     * @param contentType 上下文类型
     * @param objectName  对象名  比如: xxx/zzz/yyy/fileName.jpg
     */
    public static void uploadFile(String bucketName, String fileBase64, String contentType, String objectName) throws Exception {
        // 去掉前缀
        String prefix = fileBase64.substring(0, fileBase64.indexOf(",") + 1);
        fileBase64 = fileBase64.replace(prefix, "");
        InputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(fileBase64.replaceAll("\r\n", "")));
        uploadFile(bucketName, inputStream, contentType, objectName);
    }

    /**
     * 上传文件
     *
     * @param bucketName  桶的名字,如果桶名为null,则使用默认的桶
     * @param stream      流
     * @param contentType
     * @param objectName  对象名  比如: xxx/zzz/yyy/fileName.jpg
     */
    public static void uploadFile(String bucketName, InputStream stream, String contentType, String objectName) throws Exception {
        try {
            // 上传文件
            PutObjectArgs build = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(stream, -1, ObjectWriteArgs.MAX_PART_SIZE)
                    .contentType(contentType)
                    .build();
            minioClient.putObject(build);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("{}文件上传失败", objectName);
        } finally {
            stream.close();
        }
    }

    /**
     * 获取文件
     *
     * @param objectName
     * @return java.io.InputStream
     */
    public static InputStream getFile(String bucketName, String objectName) {
        try {
            logger.debug("bucketName = {}, objectName = {}", bucketName, objectName);
            // 文件是否存在
            StatObjectArgs statObjectArgs = StatObjectArgs.builder().bucket(bucketName).object(objectName).build();
            minioClient.statObject(statObjectArgs);
            // 获取文件
            GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            return minioClient.getObject(getObjectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("{}文件获取失败", objectName);
            return null;
        }
    }

    /**
     * 直接下载文件
     *
     * @param bucketName 桶名
     * @param res        响应
     * @param objectName 对象名
     * @param fileName   文件名
     */
    public static void lookUploadFile(String bucketName, HttpServletResponse res, String objectName, String fileName) {

        try (InputStream in = getFile(bucketName, objectName);
             OutputStream output = res.getOutputStream()) {
            // 得到输入流
            if (in == null) {
                try (PrintWriter printWriter = res.getWriter()) {
                    printWriter.append("404 - File Not Exist");
                } catch (IOException e) {
                    logger.error("数据异常: ", e);
                }
                return;
            }
            res.reset();
            // res.setContentType(getMimeType(fileName));
            // https://gitee.com/dolyw/codes/2h1r6avwxumegjs89ztyn86
            res.addHeader("content-Disposition", "inline;filename=" + java.net.URLEncoder.encode(fileName, "UTF-8"));
            byte[] b = new byte[4096];
            int i = 0;
            while ((i = in.read(b)) > 0) {
                output.write(b, 0, i);
            }
        } catch (Exception me) {
            logger.error("数据异常: ", me);
        }
    }

    /**
     * 获取外链
     *
     * @param objectName 对象名 格式为 xxx/zzz/yyy/fileName.后缀
     * @return java.lang.String
     */
    public static String getFileUrl(String bucketName, String objectName, Method method) {
        try {
            GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
                    .method(method)
                    .bucket(bucketName)
                    .expiry(60, TimeUnit.MINUTES)
                    .object(objectName)
                    .build();
            return minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
        } catch (Exception e) {
            logger.error("{}文件获取失败", objectName);
            return "";
        }
    }

    /**
     * 删除存储桶
     *
     * @param bucketName 存储桶名称
     * @return 是福哦移除成功
     */
    public static boolean removeBucket(String bucketName) throws Exception {
        boolean flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (flag) {
            Iterable> myObjects = listObjects(bucketName);
            for (Result result : myObjects) {
                Item item = result.get();
                // 有对象文件,则删除失败
                if (item.size() > 0) {
                    return false;
                }
            }
            // 删除存储桶,注意,只有存储桶为空时才能删除成功。
            RemoveBucketArgs removeBucketArgs = RemoveBucketArgs.builder().bucket(bucketName).build();
            minioClient.removeBucket(removeBucketArgs);
            flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            return !flag;

        }
        return false;
    }

    /**
     * 列出存储桶中的所有对象
     *
     * @param bucketName 存储桶名称
     * @return 对象集合
     */
    public static Iterable> listObjects(String bucketName) throws Exception {
        boolean flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (flag) {
            ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder()
                    .recursive(true)
                    .bucket(bucketName).build();
            return minioClient.listObjects(listObjectsArgs);
        }
        return null;
    }


    /**
     * 以流的形式获取一个文件对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @return {@code InputStream}
     */
    public static InputStream getObject(String bucketName, String objectName) throws Exception {
        boolean flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (flag) {
            StatObjectResponse statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.size() > 0) {
                GetObjectArgs getObjectArgs = GetObjectArgs.builder().object(objectName)
                        .bucket(bucketName).build();
                InputStream inputStream = minioClient.getObject(getObjectArgs);
                return inputStream;
            }
        }
        return null;
    }

    /**
     * 获取对象,存到本地
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param fileName   文件名路径,需要带有后缀 E:\\temp.png
     * @return 是否获取成功
     * @throws Exception 异常
     */
    public static boolean getObject(String bucketName, String objectName, String fileName) throws Exception {
        InputStream is = null;
        OutputStream os = null;

        try {
            File file = new File(fileName);
            // 文件存在就删除,重新创建,防止上次的数据影响本次
            if (file.exists()) {
                file.delete();
                file.createNewFile();
            }

            is = getObject(bucketName, objectName);
            os = Files.newOutputStream(file.toPath());
            int len = -1;
            byte[] bytes = new byte[4096];
            while ((len = is.read(bytes)) != -1) {
                os.write(bytes, 0, len);
            }
            return true;
        } finally {
            if (is != null) {
                is.close();
            }
            if (os != null) {
                os.close();
            }
        }
    }

    /**
     * 下载对象,断点续传,要求下载的文件大小不能比目标的大,否则会下载失败
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param fileName   文件名称
     * @return 是否下载成功
     */
    public static boolean downloadObject(String bucketName, String objectName, String fileName) throws Exception {
        boolean flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (flag) {
            StatObjectResponse statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.size() > 0) {
                DownloadObjectArgs downloadObjectArgs = DownloadObjectArgs.builder().object(objectName)
                        .bucket(bucketName).filename(fileName).build();
                minioClient.downloadObject(downloadObjectArgs);
                return true;
            }
        }
        return false;
    }


    /**
     * 获取对象的元数据
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @return 元数据
     */
    public static StatObjectResponse statObject(String bucketName, String objectName) throws Exception {
        boolean flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (flag) {
            StatObjectArgs statObjectArgs = StatObjectArgs.builder()
                    .object(objectName).bucket(bucketName).build();
            return minioClient.statObject(statObjectArgs);
        }
        return null;
    }

    /**
     * 删除一个对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     */
    public static boolean removeObject(String bucketName, String objectName) throws Exception {
        boolean flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (flag) {
            RemoveObjectArgs removeObject = RemoveObjectArgs.builder()
                    .bucket(bucketName).object(objectName).build();
            minioClient.removeObject(removeObject);
            return true;
        }
        return false;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy