springboot整合minio(实现文件的上传和下载超详细入门)

一、Minio介绍:

目前可用于文件存储的网络服务选择也有不少,比如阿里云OSS、七牛云、腾讯云等等,可是收费都有点小贵。为了省钱,很多公司使用MinIO做为文件服务器。

官网:MinIO | 用于AI的S3 & Kubernetes原生对象存储

 MinIO是一个开源的分布式对象存储服务器,支持S3协议并且可以在多节点上实现数据的高可用和容错。它采用Go语言开发,拥有轻量级、高性能、易部署等特点,并且可以自由选择底层存储介质。它基于Apache License 开源协议,兼容Amazon S3云存储接口。适合存储非结构化数据,如图片,音频,视频,日志等。

二、Minio的下载:

有Windows和Linux两种方式,不过我们一般把服务器下载到Linux中。使用docker来部署,能够很方便的管理(默认你的Linux中已经下载好docker了)。

//拉取镜像
docker pull quay.io/minio/minio

// 创建数据存储目录
mkdir -p ~/minio/data

// 创建minio
docker run \
   -p 9001:9000 \
   -p 9090:9090 \
   --name minio \
   -v ~/minio/data:/data \
   -e "MINIO_ROOT_USER=admin" \
   -e "MINIO_ROOT_PASSWORD=admin123456" \
   -d \
   quay.io/minio/minio server /data --console-address ":9090"

在这里解释一下这个命令:

docker pull 拉取Minio的镜像,我没有指定版本,所以默认下载最新版本。

mkdir -p ~/minio/data:创建一个文件夹来存储我们上传到Minio中的文件。

实际上,我们上传到Minio中的文件都存储到了这个文件夹中。也就是说我们上传到Minio中的文件其实都保存在你的Linux服务器中。

docker run :创建容器并运行。解释一下重要的几个参数。

9001:Minio的服务端端口

9090:Minio的客户端端口

-v ~/minio/data:/data :数据卷挂载我们之前创建的文件夹,使之成为Minio的存储容器

-e "MINIO_ROOT_USER=admin" :用户名为admin

-e "MINIO_ROOT_PASSWORD=admin123456" :密码为admin123456

将容器运行起来:

现在登录我们的客户端,并输入用户名和密码:

http://Ip地址:9090

我们还要了解一下“桶”在Minio中的概念:

"桶"(Bucket)是用来组织和管理存储的对象(文件或文件夹)的基本单位。你可以把它想象成一个容器,用来存放你的对象。

以下是关于桶的一些重要概念:

  1. 命名:每个桶都有一个全局唯一的名称。这意味着在同一个MinIO服务中,不能有两个名称相同的桶。

  2. 隔离:桶之间是完全隔离的。一个桶中的对象不能直接访问另一个桶中的对象。

  3. 访问控制:每个桶都可以有自己的访问控制策略。例如,你可以设置一个桶为公开,任何人都可以读取它的内容;也可以设置一个桶为私有,只有特定的用户可以访问它的内容。

  4. 无限制的对象存储:每个桶内可以存储无限数量的对象,只要你的存储空间足够。

  5. 元数据:每个桶都可以有一些元数据,如创建时间、修改时间等。

可以选择在客户端创建桶,也可以使用Java代码来创建,我这里就使用Java代码创建桶了。

三、使用spring boot整合Minio,完成文件的上传、下载和删除。

版本:spring boot3、jdk17、Minio的版本为:RELEASE.2023-11-01T18-37-25Z

本次使用knife4j进行测试;

创建一个空的spring boot项目,并引入如下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
        <version>4.3.0</version>
    </dependency>

    <dependency>
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
        <version>8.5.2</version>
    </dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

</dependencies>

在yml配置文件中引入如下配置:

minio:
  url: http://192.168.231.110:9001
  username: admin
  password: admin123456
  bucketName: test
spring:
  servlet:
    multipart:
      max-file-size:  10MB   # 单个文件上传的最大上限
      max-request-size:  100MB  # 整个请求体的最大上限

minio:我们自定义的属性

url:你的服务端地址

username:用户名

password:密码

bucketName:test(桶的名称,我们现在没有创建,一会用Java代码实现)

max-file-size:一定要配置这个属性,不然springboot默认的单个文件上传的最大上限为1MB,超过这个上限,就会报错。

1、创建一个实体类来继承yml文件中minio的连接信息;

@ConfigurationProperties(prefix = "minio")
@Component
@Data
public class MinioPojo {
private String url;
private String username;
private String password;
private String bucketName;
}

2、自定义bean,将MinioClient初始化;

@Configuration
public class MinioConfig {
    @Autowired
    private MinioPojo minioPojo;
@Bean
    public MinioClient minioClient(){
    return MinioClient.builder()
            .endpoint(minioPojo.getUrl())  //传入url地址
                //传入用户名和密码
            .credentials(minioPojo.getUsername(), minioPojo.getPassword())
            .build();  //完成MinioClient的初始化
    }
}

3、新创建一个MinioService类,用来进行文件的操作,并注册为bean。将来在这里进行具体代码的编写。

@Component
public class MinioService {
    @Autowired
    private MinioClient minioClient;
    @Autowired
    private MinioPojo minioPojo;

}

4、新建一个Controller,用来进行文件的上传。

@RestController
@RequestMapping("/tests")
public class TestController {
    @Autowired
    private MinioService minioService;
    @Autowired
    private MinioPojo minioPojo;
    //文件上传
    @Operation(summary = "上传图片")
    @PostMapping("/uploadImage")
    public String aa(MultipartFile file){
        String url = minioService.uploadImage(minioPojo.getBucketName(), file);
        return url;
    }

}

我们在MinioService中定义了uploadImage方法,传入了两个参数。一个是桶的名称,一个是文件file。(注意参数名称一定要叫file,因为现在前端默认传文件时都会叫这个名字,如果你随意的更改名字,后端会接收不到参数。)

返回文件在Minio中的地址url。(注意,只要你的容器Minio还在运行,就可以直接访问到。)

5、在MinioService中进行方法的实现。

 @Autowired
    private MinioClient minioClient;
    @Autowired
    private MinioProperties minioProperties;

    /*
    * 上传文件
    * */
    public String uploadFile(MultipartFile file) {
        try {
//判断桶是否存在
            boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioProperties.getBucketName()).build());
            if (!bucketExists){
//                如果不存在,就创建桶
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioProperties.getBucketName()).build());
            }
//            本地时间,具体到年、月、日
            String yyyymmdd = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
//            String uuid= UUID.randomUUID().toString();
            String filename = yyyymmdd+"/"+file.getOriginalFilename();
//          加一个/表示创建一个文件夹
            minioClient.putObject(PutObjectArgs.builder().
                    bucket(minioProperties.getBucketName()).
                    object(filename).
                    stream(file.getInputStream(), file.getSize(), -1).
//                    文件上传的类型,如果不指定,那么每次访问时都要先下载文件
        contentType(file.getContentType()).
                    build());
            String url= minioProperties.getUrl()+"/"+ minioProperties.getBucketName()+"/"+filename;
            return url;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("文件上传失败");
        }
    }

现在,我们可以在knife4j中去测试发送信息了。

如图所示,我们发送文件成功了。返回了一个url地址,我们输入这个地址就可以直接访问到这张图片了

(注意,这里有坑。我们虽然能够上传文件,并返回了文件在Minio中的地址,但是我们现在并不能直接去访问,因为现在这个桶test默认的访问权限不够,我们要在Minio的客户端将这个桶的访问权限设为public。),创建存储桶时无法直接设置为公开访问,你需要在创建存储桶后再设置存储桶的策略来实现公开访问。这是因为MinIO遵循最小权限原则,以确保数据安全。

在进行文件的上传时,如果文件名称相同会进行覆盖。这时一般的方法为在每一次文件上传时,对文件生成一个UUID,以保证每一次上传的文件名称都不一样,以免覆盖。

也可以在Java代码中实现桶访问权限的修改,但是很麻烦。

// 设置存储桶的策略

String policyJson = "{\"Statement\"[{\"Action[\"s3:GetObject\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"],\"Sid\":\"\"}],\"Version\":\"2012-10-17\"}"; minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policyJson).build());

现在,我们就可以直接输入返回的url地址,来查看图片并下载。

我这次上传的文件类型是图片,其他的文件类型也可以上传。

6、实现文件的下载:

   /*
    * 根据url 下载文件
    * */
    public String getObject(HttpServletRequest request, HttpServletResponse response,String url){
//        将传递的url 进行切割,获取到相对于桶的文件名
        String objectUrl = url.split(minioProperties.getBucketName()+"/")[1];
//        文件名
        String fileName = objectUrl.split("/")[1];
        // 设置响应的内容类型为文件类型
        response.setContentType("application/octet-stream");
        // 设置响应头,指定文件名
        response.setHeader("Content-Disposition", "attachment; filename=\"filename.extension\"");
        // 缓冲区大小
        byte[] buffer = new byte[1024];
        int bytesRead;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try{
             inputStream = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(minioProperties.getBucketName())
                    .object(objectUrl)
                    .build());
         /*   // 将输入流的内容复制到一个文件中
            String filePath = "D://"+fileName;
            Files.copy(inputStream, Path.of(filePath),
                    StandardCopyOption.REPLACE_EXISTING);
            return "文件下载成功,地址为"+filePath;*/
            // 获取输出流
             outputStream = response.getOutputStream();
            // 将输入流中的数据写入输出流
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            inputStream.close();
            outputStream.close();

        }catch (Exception e){
            e.printStackTrace();
        }
        return "文件下载失败";
    }

这里传入的url是在Minion,成功上传文件之后返回的url地址

 假如现在在Minio中有一个url地址:http://192.168.231.110:9001/test/2023-11-05/32cc63cb-eb0c-4398-8bb8-3ac37c3a130410.png

7、实现文件的删除:

    /*
    * 根据url删除文件
    * */
    public void deleteFile(String url) {
        //        将传递的url 进行切割,获取到相对于桶的文件名
        String objectUrl = url.split(minioProperties.getBucketName()+"/")[1];
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(minioProperties.getBucketName())
                    .object(objectUrl)
                    .build());

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(url+"文件删除失败");
        }
    }

文件的删除,传入上传成功的url:http://192.168.231.110:9001/test/2023-11-05/32cc63cb-eb0c-4398-8bb8-3ac37c3a130410.png

运行这个方法是在minio中将图片该删除掉了。

总结:

我们使用minio的常用操作就是,上传、下载和删除。

这些文件都存储在我们的Linux服务器中,并不能凭空存在,所以上传时要注意把握力度。如果上传的文件太多太大的话,Linux会先撑不住的。

实现Spring Boot整合MinIO实现多级目录下文件下载,可以按照以下步骤进行: 1. 引入MinIO的依赖 在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>7.1.0</version> </dependency> ``` 2. 配置MinIO连接信息 在application.yml文件中添加以下配置信息: ```yaml minio: endpoint: http://localhost:9000 # MinIO服务地址 accessKey: minioadmin # 访问Key secretKey: minioadmin # 访问Secret bucketName: test-bucket # 存储桶名称 ``` 3. 创建MinIO客户端 创建MinIO客户端的代码如下: ```java @Configuration public class MinioConfig { @Value("${minio.endpoint}") private String endpoint; @Value("${minio.accessKey}") private String accessKey; @Value("${minio.secretKey}") private String secretKey; @Value("${minio.bucketName}") private String bucketName; @Bean public MinioClient minioClient() throws InvalidPortException, InvalidEndpointException { return MinioClient.builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); } @Bean public String bucketName() { return bucketName; } } ``` 4. 实现文件下载接口 实现文件下载接口的代码如下: ```java @RestController @RequestMapping("/file") public class FileController { @Autowired private MinioClient minioClient; @Autowired private String bucketName; @GetMapping("/download") public ResponseEntity<Resource> downloadFile(@RequestParam("path") String path) throws Exception { String[] pathArr = path.split("/"); String objectName = pathArr[pathArr.length - 1]; String objectPath = path.substring(0, path.lastIndexOf("/") + 1); InputStream inputStream = minioClient.getObject(GetObjectArgs.builder() .bucket(bucketName) .object(objectPath + objectName) .build()); ByteArrayResource resource = new ByteArrayResource(inputStream.readAllBytes()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + objectName + "\"") .body(resource); } } ``` 其中,`path`参数是要下载文件路径,例如:`folder1/folder2/test.txt`。 5. 测试文件下载接口 启动应用程序后,访问`http://localhost:8080/file/download?path=folder1/folder2/test.txt`即可下载名为`test.txt`的文件,该文件位于MinIO存储桶的`folder1/folder2/`路径下。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张乔24

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值