Springboot整合Minio实现文件上传、下载

目录

前言

一、环境准备

二、环境配置

1、引入依赖

2、添加配置文件

三、代码编写

1、添加配置类

2、添加工具类

四、方法测试

1、文件上传

注意:

2、文件下载

结语


前言

springboot作为目前主流的java开发框架,用来简化spring程序的创建和开发过程。在实际开发过程中我们往往会遇到部分涉及文件上传、下载的场景。经过多方筛选最终选用了Minio作为项目与中的文件存储工具。

Minio作为一款高性能、可扩展、分布式对象存储系统,并且它是完全开源免费的一款工具,非常适合实际项目的使用!!!

关于Minio的Windows的安装教程,各位小伙伴可以参考上一篇Minio安装的教程

地址:Minio基于Window系统的安装与使用-CSDN博客

下面让我们一起来了解一下SpringBoot整合Minio的具体实现方式。

一、环境准备

在使用Minio之前我们要做的第一件事情就是有一个SpringBoot项目,需要小伙伴自行创建或者使用现有项目。土豆这里使用的是自己原有的一个若以框架改过来的一个项目,但是这个项目此前没有使用过Minio,确认这点有助于更加顺利的完成我们的测试。

如果小伙伴的项目中原本使用过Minio的,也没关系哈,可以根据本文做一个参考进一步完善项目内容。

准备好项目之后咱们就可以开始本次的任务了,请跟随土豆一起搞起来,呦吼!!!

二、环境配置

1、引入依赖

在pom.xml中引入Minio的依赖:

<dependencies>
    <!--Minio-->
    <dependency>
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
        <version>8.4.6</version>
    </dependency> 
    <!--okhttp3 依赖-->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.3</version>
        </dependency>
</dependencies>

2、添加配置文件

application.yml配置信息

# 配置minio
minio:
  server: http://localhost
  port: 9000
  endpoint: ${minio.server}:${minio.port}
  access-key: admin  # minio账号
  secret-key: admin118110  # minio密码
  bucket: demo001   # 指定桶名

三、代码编写

完成项目的基础配置之后,接下来咱开始来编写我们的代码

1、添加配置类


import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * @author YHL
 * @date 2024/4/24 9:36
 * @description 当前配置类不建议使用@Data注解,需要手动生成get,set方法
 */
@Component
@ConfigurationProperties(prefix = "minio")
public class MinIoClientConfig {

    @Value("${minio.server}")
    private String server;
    @Value("${minio.port}")
    private int port;
    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.access-key}")
    private String accessKey;
    @Value("${minio.secret-key}")
    private String secretKey;
    @Value("${minio.bucket}")
    private String bucket;

    /**
     * 创建minio连接对象
     * @return
     */
    @Bean
    public MinioClient minioClient(){
        return  MinioClient.builder()
                .endpoint(server,port,false)
                .credentials(accessKey,secretKey)
                .build();
    }

    public String getServer() {
        return server;
    }

    public void setServer(String server) {
        this.server = server;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getEndpoint() {
        return endpoint;
    }

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public String getAccessKey() {
        return accessKey;
    }

    public void setAccessKey(String accessKey) {
        this.accessKey = accessKey;
    }

    public String getSecretKey() {
        return secretKey;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }

    public String getBucket() {
        return bucket;
    }

    public void setBucket(String bucket) {
        this.bucket = bucket;
    }
}

2、添加工具类

package com.psy.common.utils;

import cn.hutool.core.util.ObjectUtil;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import org.apache.commons.lang.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author YHL
 * @date 2024/4/24 9:51
 * @description
 */
@Component
public class MinioUtil {

    @Autowired
    private MinioClient minioClient;
    @Value("${minio.bucket}")
    private String bucket;

    /**
     * description: 判断bucket是否存在,不存在则创建
     */
    @SneakyThrows
    public boolean existBucket(String name) {
        boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
        if (!exists) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
        }
        return exists;
    }

    /**
     * 创建存储bucket
     *
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    @SneakyThrows
    public Boolean makeBucket(String bucketName) {
        boolean exist = existBucket(bucketName);
        if (!exist) {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
            return true;
        }
        return false;
    }

    /**
     * 删除存储bucket
     *
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    @SneakyThrows
    public Boolean removeBucket(String bucketName) {
        boolean exist = existBucket(bucketName);
        if (!exist) return false;

        Iterable<Result<Item>> results =
                minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
        for (Result<Item> result : results) {
            Item item = result.get();
            // 桶不为空不允许删除
            if (item.size() > 0) {
                return false;
            }
        }
        minioClient.removeBucket(RemoveBucketArgs.builder()
                .bucket(bucketName)
                .build());
        return true;
    }

    /**
     * 列出所有存储桶
     */
    @SneakyThrows
    public List<Bucket> listBuckets() {
        return minioClient.listBuckets();
    }

    /**
     * 列出所有存储桶名称
     */
    public List<String> listBucketNames() {
        List<Bucket> bucketList = listBuckets();
        if (ObjectUtil.isEmpty(bucketList))
            return null;
        List<String> bucketListName = new ArrayList<>();
        for (Bucket bucket : bucketList) {
            bucketListName.add(bucket.name());
        }
        return bucketListName;
    }

    /**
     * 列出存储桶中的所有对象名称
     *
     * @param bucketName 存储桶名称
     */
    @SneakyThrows
    public List<String> listObjectNames(String bucketName) {
        boolean exist = existBucket(bucketName);
        if (!exist) return null;

        List<String> listObjectNames = new ArrayList<>();
        Iterable<Result<Item>> results =
                minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
        for (Result<Item> result : results) {
            Item item = result.get();
            listObjectNames.add(item.objectName());
        }
        return listObjectNames;
    }

    /**
     * 查看文件对象
     *
     * @param bucketName 存储bucket名称
     * @return 存储bucket内文件对象信息
     */
    public Map<String, Object> listObjects(String bucketName) {
        boolean exist = existBucket(bucketName);
        if (!exist) return null;

        Iterable<Result<Item>> results =
                minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
        Map<String, Object> map = new HashMap<>();
        try {
            for (Result<Item> result : results) {
                Item item = result.get();
                map.put(item.objectName(), item);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return map;
    }

    /**
     * 文件访问路径
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     */
    @SneakyThrows
    public String getObjectUrl(String bucketName, String objectName) {
        boolean exist = existBucket(bucketName);
        if (!exist) return null;

        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(objectName)
                        .expiry(2, TimeUnit.MINUTES)
                        .build());
    }

    /**
     * 删除一个对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     */
    @SneakyThrows
    public boolean removeObject(String bucketName, String objectName) {
        boolean exist = existBucket(bucketName);
        if (!exist) return false;

        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
        return true;
    }

    /**
     * 删除指定桶的多个文件对象
     *
     * @param bucketName  存储桶名称
     * @param objectNames 含有要删除的多个object名称的迭代器对象
     */
    @SneakyThrows
    public boolean removeObject(String bucketName, List<String> objectNames) {
        boolean exist = existBucket(bucketName);
        if (!exist) return false;

        List<DeleteObject> objects = new LinkedList<>();
        for (String objectName : objectNames) {
            objects.add(new DeleteObject(objectName));
        }
        minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build());
        return true;
    }

    /**
     * 批量删除文件对象
     *
     * @param bucketName 存储bucket名称
     * @param objects    对象名称集合
     */
    public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
        List<DeleteObject> dos = objects.stream().map(DeleteObject::new).collect(Collectors.toList());
        return minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
    }

    /**
     * 文件上传
     */
    public String upload(MultipartFile multipartFile) {
        String fileName = multipartFile.getOriginalFilename();
        // 注意,这里需要加上 \\ 将 特殊字符 . 转意 \\. ,否则异常
        assert fileName != null;
        String[] fileArray = fileName.split("\\.");

        // 获取当前日期
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String format = sdf.format(now);

        fileName = format + "/" + fileArray[0] + System.currentTimeMillis() + "." + fileArray[1];

        try {
            InputStream inputStream = multipartFile.getInputStream();
            // 上传到minio服务器
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucket)
                    .object(fileName)
                    .stream(inputStream, -1L, 10485760L)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 返回地址
        return fileName;
    }

    /**
     * 文件下载
     *
     * @param fileName 文件名
     * @param delete   是否删除
     */
    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response) {

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

            outputStream = response.getOutputStream();
            // 获取文件对象
            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucket).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);
            }
            inputStream.close();
            // 判断:下载后是否同时删除minio上的存储文件
            if (BooleanUtils.isTrue(delete)) {
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(fileName).build());
            }
        } catch (Throwable ex) {
            response.setHeader("Content-type", "text/html;charset=UTF-8");
            String data = "文件下载失败";
            try {
                OutputStream ps = response.getOutputStream();
                ps.write(data.getBytes(StandardCharsets.UTF_8));
            } catch (IOException e) {
                e.printStackTrace();
            }
        } finally {
            try {
                outputStream.close();
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在util中我们已经针对Minio的使用定义好了各种常用方法,如果有遗漏欢迎小伙伴们私信我做出补充。

四、方法测试

代码编写完成之后我们就开始测试我们代码的可用性,在这土豆简单编写了一个controller,主要用来测试我们常用的文件上传,文件下载方法。

代码如下:

import com.psy.common.core.controller.BaseController;
import com.psy.common.core.domain.AjaxResult;
import com.psy.common.utils.MinioUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

/**
 * @author YHL
 * @date 2024/4/24 8:54
 * @description
 */
@RestController
@RequestMapping("/fileDemo")
public class FileDemoController extends BaseController {

    @Resource
    private MinioUtil minioUtil;

    @RequestMapping("/upload")
    public AjaxResult upload(MultipartFile multipartFile) {
        return AjaxResult.success(minioUtil.upload(multipartFile));
    }

    @RequestMapping("/download")
    public void download(String fileName, HttpServletResponse response) {
        minioUtil.fileDownload(fileName, false, response);
    }
}

然后我们可以使用测试工具对我们的代码进行测试,查看功能是否实现。

土豆这里使用的测试工具是ApiFox,我个人觉得是这款测试工具比传统的PostMan要美观要好用,有想去的小伙伴可以尝试下载使用一下。

好了,废话不多说,咱开测。

1、文件上传

首先查看我们的Minio指定的桶的状态是否正常,可以看到咱的桶中是没有文件的:

然后我们在ApiFox中输入接口地址以及选择要上传的文件

点击发送之后,返回值显示文件上传完成

然后我们再到Minio中查看

发现所上传的图片已经上传到Minio中指定的桶中了,并且文件的上级地址是当前的日期。

所以说我们的代码是没有问题的,在实际使用中,我们肯定会有将文件地址存储起来的操作

在这里我要提醒大家,

注意:

1)为了方便以后的维护或者是数据迁移等操作,存储时只需要存储文件的相对路径,如果有需要存储桶名或者其他相关信息的话,建议区分字段存储,以避免不必要的麻烦。

2)这里文件上传使用的文件名称采用了原文件名称,但是在实际场景中,这种操作并不可取,为了避免文件冲突以及方便区分文件,建议在文件上传时在数据库中保存文件原名称,并重命名上传文件的名称(可采用时间戳等方式确保文件名称唯一)

2、文件下载

上面完成了文件上传操作后Minio中已经存在一个文件了,现在我们需要将这个文件下载下来,我们继续使用ApiFox来测试:

点击发送:

到这里之后我们的文件下载和文件上传都是成功的。

结语

以上内容就是结合上篇Minio基于Window系统的安装与使用的分享整合的SpringBoot整合Minio的内容

最后还是希望本次分享对您能够有所帮助,如有不足还请各位多多指点,多多支持。

感激不尽,感激不尽。。。。。

  • 16
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要引入minio的依赖: ``` <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.0.2</version> </dependency> ``` 然后,在application.properties中配置minio的连接信息: ``` minio.url=http://localhost:9000 minio.accessKey=minioadmin minio.secretKey=minioadmin minio.bucketName=test ``` 接着,创建一个MinioService类,用于处理文件上传下载: ``` @Service public class MinioService { @Value("${minio.url}") private String minioUrl; @Value("${minio.accessKey}") private String accessKey; @Value("${minio.secretKey}") private String secretKey; @Value("${minio.bucketName}") private String bucketName; private final MinioClient minioClient = new MinioClient(minioUrl, accessKey, secretKey); public void upload(MultipartFile file) throws Exception { // 生成文件名 String originalFilename = file.getOriginalFilename(); String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf(".")); // 上传文件 minioClient.putObject(PutObjectArgs.builder() .bucket(bucketName) .object(fileName) .stream(file.getInputStream(), file.getSize(), -1) .contentType(file.getContentType()) .build()); } public InputStream download(String fileName) throws Exception { // 下载文件 return minioClient.getObject(GetObjectArgs.builder() .bucket(bucketName) .object(fileName) .build()); } } ``` 最后,在Controller中使用MinioService处理上传下载请求: ``` @RestController public class MinioController { @Autowired private MinioService minioService; @PostMapping("/upload") public String upload(@RequestParam("file") MultipartFile[] files) { try { for (MultipartFile file : files) { minioService.upload(file); } return "上传成功"; } catch (Exception e) { e.printStackTrace(); return "上传失败"; } } @GetMapping("/download") public ResponseEntity<InputStreamResource> download(@RequestParam("fileName") String fileName) { try { InputStream inputStream = minioService.download(fileName); InputStreamResource inputStreamResource = new InputStreamResource(inputStream); HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attachment; filename=" + fileName); return ResponseEntity.ok().headers(headers).contentLength(inputStream.available()) .contentType(MediaType.parseMediaType("application/octet-stream")).body(inputStreamResource); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); } } } ``` 这样,就可以使用Spring BootMinio实现多文件批量上传了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值