SpringBoot 对接 MinIO 实现文件上传下载删除

2023年 10月 10日 45.1k 0

前言

MinIO 是一个开源的对象存储服务器,它可以存储大容量非结构化的数据,例如图片、音频、视频、日志文件、备份数据和容器/虚拟机镜像等。

Spring Boot 与 MinIO 的整合可以方便地实现文件的上传和下载等功能

在实际应用中,Spring Boot 对接 MinIO 可以用于以下场景:

  • 文件存储:将文件存储在 MinIO 中,以便在需要时进行访问和下载。
  • 数据备份:将数据备份到 MinIO 中,以便在需要时进行恢复。
  • 容器镜像存储:将 Docker 容器镜像存储在 MinIO 中,以便在需要时进行访问和下载。
  • 静态资源存储:将静态资源(如 CSS、JavaScript 和图像)存储在 MinIO 中,以便在需要时进行访问和下载。

通过 Spring Boot 对接 MinIO,你可以方便地管理和操作文件存储,并且可以根据自己的需求进行扩展和定制化

MinIO 安装

MinIO 安装参考:Linux 部署 MinIO 分布式对象存储 & 配置为 typora 图床

引入依赖

maven 依赖


    io.minio
    minio
    8.5.2

配置

配置文件

在 application.yml 中添加

minio:
  endpoint: http://192.168.101.3:9001
  access-key: DHgpKILqY5iNa88LByHx  # minio 的 accessKey
  secret-key: Ppa9Nxes7jOkbBD2BTuFhjFcfGjw3lXmXUItKcBV  # minio 的 secretKey

创建配置类

对应配置文件

@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioInfo {
 
    private String endpoint;
 
    private String accessKey;
 
    private String secretKey;
}

配置客户端

配置 minio 操作客户端

@Configuration
@EnableConfigurationProperties(MinioInfo.class)
public class MinioConfig {
 
    @Autowired
    private MinioInfo minioInfo;
 
    /**
     * 获取 minioClient
     */
    @Bean
    public MinioClient minioClient() throws NoSuchAlgorithmException, KeyManagementException {
        return MinioClient.builder().endpoint(minioInfo.getEndpoint())
                .credentials(minioInfo.getAccessKey(),minioInfo.getSecretKey())
                .build();
   }
}

工具类封装

@Slf4j
@Component
public class MinioUtils {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioInfo minioInfo;


    /**
     * 上传文件
     *
     * @param file       文件信息
     * @param bucketName minio bucket 名称
     * @return 上传文件路径
     */
    public String uploadFile(MultipartFile file, String bucketName) {
        if (file == null || file.getSize() == 0) {
            log.error("minio 上传文件:{}", "上传文件不能为空");
            return null;
        }
        try {
            // 判断是否存在
            createBucket(bucketName);
            // 原文件名
            String originalFilename = file.getOriginalFilename();
            minioClient.putObject(
                    PutObjectArgs.builder().bucket(bucketName).object(originalFilename).stream(
                            file.getInputStream(), file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build());
            return minioInfo.getEndpoint() + "/" + bucketName + "/" + originalFilename;
        } catch (Exception e) {
            log.error("minio 上传失败:{}", e.getMessage());
        }
        log.error("minio 上传文件:{}", "上传失败");
        return null;
    }

    /**
     * 通过字节流上传
     *
     * @param imageFullPath 图片路径
     * @param bucketName    minio bucket 名称
     * @param imageData     图片数据
     * @return 上传文件路径
     */
    public String uploadImage(String imageFullPath,
                              String bucketName,
                              byte[] imageData) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imageData);
        try {
            //判断是否存在
            createBucket(bucketName);
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(imageFullPath)
                    .stream(byteArrayInputStream, byteArrayInputStream.available(), -1)
                    .contentType(".jpg")
                    .build());
            return minioInfo.getEndpoint() + "/" + bucketName + "/" + imageFullPath;
        } catch (Exception e) {
            log.error("minio 上传失败:{}", e.getMessage());
        }
        log.error("minio 上传失败:{}", "上传失败");
        return null;
    }

    /**
     * 删除文件
     *
     * @param bucketName minio bucket 名称
     * @param fileName 文件名
     * @return
     */
    public Boolean removeFile(String bucketName, String fileName) {
        try {
            //判断桶是否存在
            boolean res = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (res) {
                //删除文件
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName)
                        .object(fileName).build());
            }
        } catch (Exception e) {
            log.error("minio 删除文件失败");
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 下载文件
     *
     * @param fileName 文件名
     * @param bucketName minio bucket 名称
     * @param response 请求响应
     */
    public void fileDownload(String fileName,
                             String bucketName,
                             HttpServletResponse response) {

        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            if (StringUtils.isBlank(fileName)) {
                response.setHeader("Content-type", "text/html;");
                String data = "文件下载失败";
                OutputStream ps = response.getOutputStream();
                ps.write(data.getBytes("UTF-8"));
                return;
            }

            outputStream = response.getOutputStream();
            // 获取文件对象
            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            byte buf[] = new byte[1024];
            int length = 0;
            response.reset();
            response.setHeader("Content-Disposition", "attachment;filename=" +
                    URLEncoder.encode(fileName.substring(fileName.lastIndexOf("/") + 1), "UTF-8"));
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("UTF-8");
            // 输出文件
            while ((length = inputStream.read(buf)) > 0) {
                outputStream.write(buf, 0, length);
            }
            System.out.println("下载成功");
            inputStream.close();
        } catch (Throwable ex) {
            response.setHeader("Content-type", "text/html;charset=UTF-8");
            String data = "文件下载失败";
            try {
                OutputStream ps = response.getOutputStream();
                ps.write(data.getBytes("UTF-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        } finally {
            try {
                outputStream.close();
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @SneakyThrows
    public void createBucket(String bucketName) {
        // 不存在就创建
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }
}

使用

上传文件

@RestController
@RequestMapping("/minio")
public class TestMinioController {

    @Resource
    private MinioUtils minioUtils;

    /**
     * 上传文件
     * @param file 文件
     * @return 路径
     */
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public BaseResponse uploadByMinio(@RequestParam(name = "file") MultipartFile file) {
        //返回存储路径
        String path = minioUtils.uploadFile(file, "minio-img");
        return ResultUtils.success(path);
    }
}

请求参数为 file,类型为 file,响应回来的 data 为文件路径

image-20231009150510183

请求成功后,可以在 minio 中查看

image-20231009150620190

下载文件

根据文件名下载文件

/**
 * 根据文件名下载文件
 *
 * @param fileName 文件名
 * @param response 请求响应体
 */
@GetMapping("/download")
public void downloadByMinio(@RequestParam(name = "fileName") String fileName,
                            HttpServletResponse response) {
    try {
        minioUtils.fileDownload(fileName, "minio-img", response);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

请求参数为 fileName

image-20231009151358994

删除文件

/**
 * 根据文件名删除文件
 *
 * @param fileName 文件名
 * @return 是否成功
 */
@PostMapping("/delete")
@ResponseBody
public BaseResponse deleteByMonio(String fileName) {
    Boolean result = minioUtils.removeFile("minio-img", fileName);
    if (!result) {
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR,"删除失败");
    }
    return ResultUtils.success("删除成功");
}

image-20231009151511984

相关文章

服务器端口转发,带你了解服务器端口转发
服务器开放端口,服务器开放端口的步骤
产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
如何使用 WinGet 下载 Microsoft Store 应用
百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

发布评论