Minio 工具类

Minio作为高效存储的系统,提供增删改查的一些方法,工具类如下:



import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import io.minio.BucketExistsArgs;
import io.minio.GetObjectArgs;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.ListObjectsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.RemoveObjectsArgs;
import io.minio.Result;
import io.minio.SetBucketPolicyArgs;
import io.minio.http.Method;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
@Component
public class MinioUtil {

    private static final MinioClient client = SpringUtil.getBean(MinioClient.class);


//    @Autowired
//    private MinioProperties minioProperties;

    private static final String BUCKET_PARAM = "${bucket}";

    /**
     * bucket权限-读写
     */
    private static final String READ_WRITE = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";

    /**
     * 获取URL
     *
     * @param objectName
     * @param bucketName
     * @param time
     * @param timeUnit
     * @return
     */
    public static String getUrl(String objectName, String bucketName, int time, TimeUnit timeUnit) {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }
        String url = null;
        try {
            url = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(time, timeUnit).build());
        }
        catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return url;
    }

    /**
     * 创建bucket
     */
    public static void createBucket(String bucketName) throws Exception {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }
        if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
            client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            createBucketPolicy(bucketName);
        }
    }


    /**
     * 设置桶策略
     *
     * @param bucketName 桶名称
     */
    @SneakyThrows(Exception.class)
    public static void createBucketPolicy(String bucketName) {
        client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(READ_WRITE.replace(BUCKET_PARAM, bucketName)).build());
    }


    /**
     * 上传文件
     */
    public static Map uploadFile(MultipartFile file, String bucketName) throws Exception {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }
        Map<String, Object> map = Maps.newHashMap();
        //判断文件是否为空
        if (null == file || 0 == file.getSize()) {
            return null;
        }
        //判断存储桶是否存在  不存在则创建
        createBucket(bucketName);
        //上传到桶中的文件的路径 使用当前日期作为文件存储路径
        String data = DateUtil.format(new Date(), "yyyyMMdd") + StrUtil.SLASH;
        //文件名
        String originalFilename = file.getOriginalFilename();
        assert originalFilename != null;
        //开始上传
        client.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(data + originalFilename).stream(
                                file.getInputStream(), file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build());
        String url = SpringUtil.getBean(MinioProperties.class).getUrl() + StrUtil.SLASH + bucketName + StrUtil.SLASH + data + originalFilename;
        //fileName /路径/文件名
        String fileName = StrUtil.SLASH + data + originalFilename;
        log.info("上传文件成功url :[{}],", url);
        map.put("url", url);
        map.put("fileName", fileName);
        return map;
    }

    /**
     * Minio上传文件 通用
     *
     * @param localFilePath 文件路径
     * @param bucketName    桶的名字
     * @param contentType   文件类型,image/jpeg,video/mp4
     * @return
     * @throws Exception
     */
    public static MinioUploadResponseDto uploadLocalFile(String localFilePath, String bucketName, String contentType) throws Exception {

        if (StrUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }

        // 判断文件是否为空
        Path filePath = Paths.get(localFilePath);
        Assert.isTrue(Files.exists(filePath), "File does not exist: " + localFilePath);

        // 判断存储桶是否存在,不存在则创建
        createBucket(bucketName);

        /**
         *
         * 上传到桶中的文件的路径,使用当前日期作为文件存储路径
         * data代表的是一个日期或者其他类型的前缀,从存储桶根目录开始创建这个前缀,不需要在data前面添加斜杠,是虚拟的相对路径
         */
        String data = DateUtil.format(new Date(), "yyyyMMdd") + File.separator;

        // 文件名
        String originalFilename = filePath.getFileName().toString();

        // 获取内容类型,如果为 null,则设置默认类型
        if (StringUtils.isBlank(contentType)) {
            contentType = Files.probeContentType(filePath);
            if (null == contentType) {
                log.warn("Content type is not detected, using default: application/octet-stream");
                contentType = "application/octet-stream";
            }

        }

        // 开始上传
        client.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(data + originalFilename)
                .stream(Files.newInputStream(filePath), Files.size(filePath), -1)
                .contentType(contentType)
                .build());

        String url = SpringUtil.getBean(MinioProperties.class).getUrl() + File.separator + bucketName + File.separator + data + originalFilename;
        // fileName :不要加/,否则会多拼接 ,路径/文件名
        String fileName = data + originalFilename;
        log.info("上传文件成功url :[{}]", url);

        MinioUploadResponseDto minioUploadResponseDto = new MinioUploadResponseDto();

        minioUploadResponseDto.setUrl(url);
        minioUploadResponseDto.setFileName(fileName);

        return minioUploadResponseDto;
    }

    /**
     * 上传文件
     *
     * @param file 文件
     * @return {@link String}
     * @throws Exception 异常
     */
    public static String uploadFile(MultipartFile file) throws Exception {
        String bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        //判断文件是否为空
        if (null == file || 0 == file.getSize()) {
            return null;
        }
        //判断存储桶是否存在  不存在则创建
        createBucket(bucketName);
        //上传到桶中的文件的路径 使用当前日期作为文件存储路径
        String data = DateUtil.format(new Date(), "yyyyMMdd") + StrUtil.SLASH;
        //文件名
        String originalFilename = file.getOriginalFilename() + IdUtil.fastSimpleUUID();
        //开始上传
        client.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(data + originalFilename).stream(
                                file.getInputStream(), file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build());
        String url = SpringUtil.getBean(MinioProperties.class).getUrl() + StrUtil.SLASH + bucketName + StrUtil.SLASH + data + originalFilename;
        log.info("上传文件成功url :[{}],", url);
        return url;
    }

    /**
     * 删除一个对象
     */
    public static void deleteObject(String bucketName, String objectName) throws Exception {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }
        client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }


    /**
     * 批量删除
     */
    public static void deleteObjects(String bucketName, List<String> objectNames) throws Exception {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }
        List<DeleteObject> objects = objectNames.stream().map(DeleteObject::new).collect(Collectors.toList());
        // 构建RemoveObjectsArgs实例
        RemoveObjectsArgs removeObjectsArgs = RemoveObjectsArgs.builder()
                .bucket(bucketName)
                .objects(objects)
                .build();
        client.removeObjects(removeObjectsArgs);
    }


    /**
     * 根据桶名称删除截止时间之前的数据
     *
     * @param bucketName 桶的名字
     * @param cutoffDate 截止时间
     * @throws Exception
     */
    public static void deleteObjectsByBucketName(String bucketName, Instant cutoffDate) throws Exception {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }
        //列出桶中的所有对象
        //recursive参数设置为true,以递归地列出所有子文件夹中的对象。
        Iterable<Result<Item>> results = client.listObjects(ListObjectsArgs.builder().bucket(bucketName).recursive(true).build());

        for (Result<Item> result : results) {
            Item item = result.get();
            // 获取对象的最后修改时间
//            ZonedDateTime zonedDateTime = item.lastModified();
//            log.info("item name:{}", JSONObject.toJSONString(item));
            //item name:{"deleteMarker":false,"dir":true,"latest":false}
            if (!item.isDir()) {
                Instant lastModified = item.lastModified().toInstant();
                if (lastModified.isBefore(cutoffDate)) {
                    // 如果对象的最后修改时间早于指定日期,则删除
                    // 构建RemoveObjectsArgs实例
                    RemoveObjectArgs removeObjectsArgs = RemoveObjectArgs.builder().bucket(bucketName).object(item.objectName()).build();
                    client.removeObject(removeObjectsArgs);
                    log.info("Deleted object:{}", item.objectName());
                }

            }

        }
    }


    /**
     * 下载一个文件
     */
    public static InputStream download(String bucketName, String objectName) {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }
        try {
            return client.getObject(
                    GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
        }
        catch (Exception e) {
            throw new ApiException("下载文件失败");
        }
    }

    /**
     * 下载视频
     *
     * @param bucketName
     * @param objectName
     * @return
     */
    public static InputStream downloadVideo(String bucketName, String objectName) {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }
        try {
            return client.getObject(
                    GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
        }
        catch (Exception e) {
            throw new ApiException("下载文件失败");
        }
    }

    /**
     * 获取图片;列表
     *
     * @param bucketName
     * @param objectNameList
     * @return
     */
    public static ImageResponse downloadList(String bucketName, List<String> objectNameList) {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }

        ImageResponse response = new ImageResponse();
        try {

            for (String imageName : objectNameList) {

                try (InputStream inputStream = client.getObject(
                        GetObjectArgs.builder().bucket(bucketName).object(imageName).build())) {

                    //                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//                byte[] buffer = new byte[4096];
//                int n = 0;
//                while (-1 != (n = inputStream.read(buffer))) {
//                    outputStream.write(buffer, 0, n);
//                }

                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ImageIO.write(ImageIO.read(inputStream), "jpg", baos);
                    response.getImageData().put(imageName, baos.toByteArray());

                }

            }
        }
        catch (Exception e) {
            throw new ApiException("下载文件失败");
        }

        return response;
    }

    /**
     * 下载资源
     *
     * @param bucketName
     * @param objectNameList
     * @return
     */
    public static ResponseEntity<InputStreamResource> download(String bucketName, List<String> objectNameList) {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {

            for (String objectName : objectNameList) {
                // 从MinIO获取对象
                try (InputStream inputStream = client.getObject(
                        GetObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .build())) {

                    // 创建ZIP条目
                    ZipEntry zipEntry = new ZipEntry(Paths.get(objectName).getFileName().toString());
                    zipEntry.setMethod(ZipEntry.DEFLATED); // 设置压缩方法
                    zipOutputStream.putNextEntry(zipEntry);

                    // 将输入流复制到ZIP输出流
//                    IOUtils.copy(inputStream, zipOutputStream);
                    byte[] buffer = new byte[4096];
                    int n = 0;
                    while (-1 != (n = inputStream.read(buffer))) {
                        zipOutputStream.write(buffer, 0, n);
                    }

                    zipOutputStream.closeEntry();

                }
                catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
            // 刷新缓冲区,确保所有数据都被写入
            zipOutputStream.finish();
            zipOutputStream.flush();
            zipOutputStream.close();


            // 设置响应头
            HttpHeaders headers = new HttpHeaders();
            String name = "attachment;filename=" + DateUtils.getYyyyMMddHHmmss() + ".zip";
            headers.add(HttpHeaders.CONTENT_DISPOSITION, name);
            headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(byteArrayOutputStream.size()));

            // 返回ZIP文件作为附件
            return ResponseEntity.ok()
                    .headers(headers)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
//                    .contentType(new MediaType("application", "zip", StandardCharsets.UTF_8))
                    .body(new InputStreamResource(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())));
        }
        catch (Exception e) {
            log.error("下载附件失败", e);
        }
        return null;
    }

    /**
     * 文件系统压缩
     *
     * @param bucketName
     * @param objectNameList
     * @return
     */
    public static ResponseEntity<FileSystemResource> downloadResource(String bucketName, List<String> objectNameList) {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }

        File tempFile = null;
        try {
            // 创建临时文件
            tempFile = File.createTempFile("temp", ".zip");

            try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
                 ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream, StandardCharsets.UTF_8)) {

                for (String objectName : objectNameList) {
                    // 从MinIO获取对象
                    try (InputStream inputStream = client.getObject(
                            GetObjectArgs.builder()
                                    .bucket(bucketName)
                                    .object(objectName)
                                    .build())) {

                        // 创建ZIP条目
                        ZipEntry zipEntry = new ZipEntry(Paths.get(objectName).getFileName().toString());
                        zipEntry.setMethod(ZipEntry.DEFLATED); // 设置压缩方法
                        zipOutputStream.putNextEntry(zipEntry);

                        // 将输入流复制到ZIP输出流
//                        IOUtils.copy(inputStream, zipOutputStream);

                        byte[] buffer = new byte[4096];
                        int n = 0;
                        while (-1 != (n = inputStream.read(buffer))) {
                            zipOutputStream.write(buffer, 0, n);
                        }

                        zipOutputStream.closeEntry(); // 结束当前条目
                    }
                    catch (Exception e) {
                        log.error(e.getMessage(), e);
                    }
                }

                // 刷新缓冲区,确保所有数据都被写入
                zipOutputStream.finish();
                zipOutputStream.flush();
                zipOutputStream.close();


                // 设置响应头
                HttpHeaders headers = new HttpHeaders();
                String name = "attachment; filename=" + DateUtils.getYyyyMMddHHmmss() + ".zip";
                headers.add(HttpHeaders.CONTENT_DISPOSITION, name);
                headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(tempFile.length()));

                // 返回ZIP文件作为附件
                return ResponseEntity.ok()
                        .headers(headers)
//                        .contentType(MediaType.APPLICATION_OCTET_STREAM)
                        .contentType(new MediaType("application", "zip", StandardCharsets.UTF_8))
                        .body(new FileSystemResource(tempFile));
            }
        }
        catch (Exception e) {
            log.error("下载附件失败", e);
            if (tempFile != null && tempFile.exists()) {
                tempFile.delete(); // 清理临时文件
            }
        }
        finally {
            // 异步删除临时文件
            File finalTempFile = tempFile;
            CompletableFuture.runAsync(() -> {
                if (finalTempFile != null && finalTempFile.exists()) {
                    boolean deleted = finalTempFile.delete();
                    if (!deleted) {
                        log.error("Failed to delete temporary file:{} ", finalTempFile.getAbsolutePath());
                    }
                    else {
                        log.info("Temporary file deleted:{}", finalTempFile.getAbsolutePath());
                    }
                }
            });
        }
        return null;
    }

    /**
     * 下载图片
     *
     * @param bucketName
     * @param objectNameList
     * @return
     */
    public static ResponseEntity<InputStreamResource> downloadImage(String bucketName, List<String> objectNameList) {
        if (CharSequenceUtil.isBlank(bucketName)) {
            bucketName = SpringUtil.getBean(MinioProperties.class).getBucketName();
        }
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {

            for (String objectName : objectNameList) {
                // 从MinIO获取对象
                try (InputStream inputStream = client.getObject(
                        GetObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .build())) {

                    // 创建ZIP条目
                    ZipEntry zipEntry = new ZipEntry(Paths.get(objectName).getFileName().toString());
                    zipEntry.setMethod(ZipEntry.DEFLATED); // 设置压缩方法
                    zipOutputStream.putNextEntry(zipEntry);

                    // 将输入流复制到ZIP输出流
                    IOUtils.copy(inputStream, zipOutputStream);
                    zipOutputStream.closeEntry();

                    // 关闭输入流
//                    inputStream.close();
                }
            }
            // 刷新缓冲区,确保所有数据都被写入
            zipOutputStream.finish();
            zipOutputStream.flush();
            zipOutputStream.close();

            // 设置响应头
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=images.zip");
            headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(byteArrayOutputStream.size()));

            // 返回ZIP文件作为附件
            return ResponseEntity.ok()
                    .headers(headers)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(new InputStreamResource(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())));
        }
        catch (Exception e) {
            throw new ApiException("下载图片失败");
        }

    }


    public static String exportAndZipFiles(String bucketName, String zipFileName, String... fileNames) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {

            for (String fileName : fileNames) {
                try (InputStream objectStream = client.getObject(
                        GetObjectArgs.builder().bucket(bucketName).object(fileName).build())) {

                    ZipEntry zipEntry = new ZipEntry(fileName);
                    zipOutputStream.putNextEntry(zipEntry);

                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = objectStream.read(buffer)) > 0) {
                        zipOutputStream.write(buffer, 0, length);
                    }
                    zipOutputStream.closeEntry();
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
            zipOutputStream.finish();
            try (InputStream zipInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray())) {
                client.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucketName)
                                .object(zipFileName)
                                .stream(zipInputStream, byteArrayOutputStream.size(), -1)
                                .contentType("application/zip")
                                .build()
                );
            }
            catch (Exception e) {
                log.error("exportAndZipFiles error:{}", e.getMessage());
            }
        }
        catch (IOException e) {
            log.error("exportAndZipFiles error:{}", e.getMessage());
        }
        return MinioUtil.getUrl(zipFileName, bucketName, 7, TimeUnit.DAYS);
    }


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值