Spring Boot | 基于MinIO实现文件上传和下载

关注:CodingTechWork

介绍

在现代的 web 应用中,文件上传和下载是常见的需求。MinIO 是一个开源的高性能分布式对象存储服务,可以用来存储和管理大量的非结构化数据,如图片、视频、日志文件等。本文将介绍如何在 Spring Boot 应用中,结合 MinIO 来实现文件的上传和下载功能,并使用 Feign 客户端进行远程调用文件上传和下载的服务。

MinIO 相关介绍

MinIO 是一个高性能的分布式对象存储系统,兼容 Amazon S3 API,通常用于存储和管理大量非结构化数据,如图片、视频、备份、日志等。它提供了与 S3 完全兼容的 API,使得开发者能够使用相同的工具和库与其进行交互。MinIO 被设计为云原生,适用于在 Docker、Kubernetes 和虚拟机中运行。

MinIO 特点

  1. 高性能:支持高吞吐量、高并发的读写操作,适用于需要大规模数据存储的应用。
  2. S3 兼容:MinIO 实现了完整的 Amazon S3 API,支持通过现有的 S3 客户端、SDK 和工具来进行交互。
  3. 高可扩展性:支持水平扩展,支持分布式部署,能够在多台机器上运行并提供统一的对象存储服务。
  4. 轻量级:MinIO 是一个轻量级的应用程序,易于部署和管理,可以在资源受限的环境中运行(如单机或者小型集群)。
  5. 支持对象加密:MinIO 支持对象级加密,可以为上传到存储中的对象加密,保证数据安全。
  6. 支持版本控制:MinIO 支持对象版本管理,允许对存储中的文件进行版本控制。

MinIO 核心概念

桶(Bucket)

  1. 在 MinIO 中,桶类似于文件系统中的文件夹。每个桶用于存储一组对象。
  2. 每个桶都可以有独立的权限配置。
  3. 存储在桶中的对象有唯一的标识符(通常是对象的文件名)。

对象(Object)

  1. 对象是 MinIO 存储的基本单位,类似于文件系统中的文件。
  2. 对象由数据和元数据组成,可以是任何类型的数据,如文本、图像、视频等。

对象键(Object Key)

  1. 对象键是对象在桶中的唯一标识符,通常对应于文件名。

访问密钥和密钥(Access Key & Secret Key)

  1. MinIO 使用访问密钥(Access Key)和密钥(Secret Key)进行身份验证,类似于 S3。
  2. 用户可以配置访问密钥和密钥以确保数据的访问权限。

MinIO API 介绍

MinIO 的 API 设计遵循 S3 API,几乎所有 S3 的 API 都可以在 MinIO 中使用。以下是 MinIO 支持的常见操作和 API:

Bucket 操作

  1. 创建桶(Create Bucket):创建一个新桶来存储对象。
java 复制代码
minioClient.makeBucket("mybucket");
  1. 列举桶(List Buckets):获取当前 MinIO 实例中所有存在的桶。
java 复制代码
List<Bucket> buckets = minioClient.listBuckets();
  1. 检查桶是否存在(Bucket Exists):检查桶是否存在。
java 复制代码
boolean exists = minioClient.bucketExists("mybucket");
  1. 删除桶(Delete Bucket):删除桶(桶必须为空才能删除)。
java 复制代码
minioClient.removeBucket("mybucket");

对象操作

  1. 上传对象(Put Object):上传一个文件到 MinIO 中指定的桶。
java 复制代码
minioClient.putObject("mybucket", "myfile.txt", fileInputStream, fileSize, null, null, "application/octet-stream");
  1. 获取对象(Get Object):从指定桶中获取对象内容。
java 复制代码
InputStream inputStream = minioClient.getObject("mybucket", "myfile.txt");
  1. 删除对象(Remove Object):删除 MinIO 存储桶中的某个对象。
java 复制代码
minioClient.removeObject("mybucket", "myfile.txt");
  1. 列举对象(List Objects):列举桶中的所有对象(可以按前缀过滤)。
java 复制代码
Iterable<Result<Item>> objects = minioClient.listObjects("mybucket");
for (Result<Item> result : objects) {
    Item item = result.get();
    System.out.println("Object name: " + item.objectName());
}
  1. 获取对象元数据(Stat Object):获取对象的元数据,如大小、最后修改时间等。
java 复制代码
StatObjectResponse stat = minioClient.statObject("mybucket", "myfile.txt");
System.out.println("Object size: " + stat.size());

文件下载

  1. 下载对象(Get Object):从 MinIO 中下载一个对象,并将其存储到本地文件系统。
java 复制代码
minioClient.getObject("mybucket", "myfile.txt", Paths.get("/path/to/destination"));

对象版本控制

  1. 启用版本控制:启用桶的版本控制功能。
java 复制代码
minioClient.enableBucketVersioning("mybucket");
  1. 获取对象版本(Get Object Version):获取对象的指定版本。
java 复制代码
InputStream inputStream = minioClient.getObject("mybucket", "myfile.txt", "versionId");
  1. 删除对象版本(Remove Object Version):删除某个版本的对象。
java 复制代码
minioClient.removeObject("mybucket", "myfile.txt", "versionId");

权限管理

MinIO 支持基于用户的权限控制,可以通过配置桶的访问策略来管理谁可以访问存储中的对象。

  1. 设置桶的访问权限(Bucket Policy):设置桶的访问权限来限制用户对对象的访问。
java 复制代码
String policy = "{ \"Version\": \"2025-01-07\", \"Statement\": [ { \"Effect\": \"Allow\", \"Principal\": { \"AWS\": \"*\" }, \"Action\": [ \"s3:GetObject\" ], \"Resource\": [ \"arn:aws:s3:::mybucket/*\" ] } ] }";
minioClient.setBucketPolicy("mybucket", policy);

MinIO 安全性特性

  1. 加密:
    MinIO 支持两种类型的加密:服务端加密(SSE)客户端加密(CSE)。服务端加密会在对象上传到 MinIO 时自动对其进行加密。
  2. 身份验证与授权:
    MinIO 使用访问密钥(Access Key)密钥(Secret Key)来进行身份验证。用户可以通过配置 MinIO 的访问控制列表(ACL)来限制谁可以访问桶和对象。
  3. TLS/SSL 支持:
    MinIO 支持通过 TLS(即 HTTPS)加密通信来保护数据传输安全。

项目结构

本项目的文件上传和下载功能将分成两个部分:

  1. minio 服务端(本地服务处理文件上传)

  2. Feign 客户端(模拟远程文件上传请求)

    file-upload-demo

    ├── src/main/java/com/example/fileupload
    │ ├── controller
    │ │ └── FileController.java
    │ ├── service
    │ │ └── FileService.java
    │ ├── feign
    │ │ └── FileFeignClient.java
    │ ├── FileUploadDemoApplication.java
    │ └── ...
    ├── pom.xml
    └── resources
    └── application.properties

Maven 依赖

在 pom.xml 中添加以下依赖:

xml 复制代码
<dependencies>
    <!-- Spring Boot Web 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Feign 依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    <!-- minio 客户端依赖 -->
    <dependency>
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
        <version>8.3.5</version>
    </dependency>

    <!-- 文件上传需要的依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
</dependencies>

同时,在 application.properties 中配置文件上传大小限制:

properties配置文件

properties 复制代码
# MinIO 配置
minio.url=http://localhost:9000
minio.access-key=your-access-key
minio.secret-key=your-secret-key
minio.bucket-name=mybucket

# 文件上传大小限制
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

代码示例

MinIO 配置

创建一个 MinIO 配置类来初始化 MinIO 客户端。

MinioConfig.java

java 复制代码
package com.example.fileupload.config;

import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MinioConfig {

    @Value("${minio.url}")
    private String minioUrl;

    @Value("${minio.access-key}")
    private String accessKey;

    @Value("${minio.secret-key}")
    private String secretKey;

    @Value("${minio.bucket-name}")
    private String bucketName;

    @Bean
    public MinioClient minioClient() {
        try {
            MinioClient minioClient = MinioClient.builder()
                    .endpoint(minioUrl)
                    .credentials(accessKey, secretKey)
                    .build();

            // 如果桶不存在则创建桶
            boolean isExist = minioClient.bucketExists(bucketName);
            if (!isExist) {
                minioClient.makeBucket(bucketName);
            }
            return minioClient;
        } catch (Exception e) {
            throw new RuntimeException("初始化 MinIO 客户端失败", e);
        }
    }
}

Controller 层:文件上传与下载接口

在 Controller 层,我们定义文件上传和下载的接口。上传文件时将文件存储到 MinIO 中,下载文件时从 MinIO 拉取文件。

FileController.java

java 复制代码
package com.example.fileupload.controller;

import com.example.fileupload.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@RestController
@RequestMapping("/api/files")
public class FileController {

    @Autowired
    private FileService fileService;

    // 文件上传接口
    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            String fileUrl = fileService.uploadFile(file);
            return ResponseEntity.ok("文件上传成功: " + fileUrl);
        } catch (IOException e) {
            return ResponseEntity.status(500).body("文件上传失败: " + e.getMessage());
        }
    }

    // 文件下载接口
    @GetMapping("/download/{filename}")
    public ResponseEntity<InputStreamResource> downloadFile(@PathVariable String filename) {
        try {
            InputStreamResource resource = fileService.downloadFile(filename);
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + filename)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(resource);
        } catch (IOException e) {
            return ResponseEntity.status(500).body(null);
        }
    }
}

Service 层:文件处理业务

在 Service 层,我们定义实际的文件处理逻辑,包括文件上传和下载。我们使用 MultipartFile 来接收文件,并将其保存到服务器上。

FileService.java

java 复制代码
package com.example.fileupload.service;

import io.minio.MinioClient;
import io.minio.errors.MinioException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;

@Service
public class FileService {

    @Autowired
    private MinioClient minioClient;

    private final String bucketName = "mybucket";  // 从配置中加载桶名

    // 文件上传方法
    public String uploadFile(MultipartFile file) throws IOException {
        try {
            // 获取文件输入流
            InputStream fileInputStream = file.getInputStream();
            // 上传文件到 MinIO
            minioClient.putObject(bucketName, file.getOriginalFilename(), fileInputStream, file.getSize(), null, null, "application/octet-stream");
            return "http://localhost:9000/" + bucketName + "/" + file.getOriginalFilename();  // MinIO 文件 URL
        } catch (MinioException | IOException e) {
            throw new IOException("上传文件失败", e);
        }
    }

    // 文件下载方法
    public InputStreamResource downloadFile(String filename) throws IOException {
        try {
            // 获取文件输入流
            InputStream fileInputStream = minioClient.getObject(bucketName, filename);
            return new InputStreamResource(fileInputStream);
        } catch (MinioException | IOException e) {
            throw new IOException("下载文件失败", e);
        }
    }
}

Feign 客户端:远程调用文件上传下载服务

Feign 是一个声明式的 HTTP 客户端,它简化了 HTTP 请求的调用过程。在这个例子中,我们使用 Feign 来调用远程的文件上传和下载接口。

FileFeignClient.java

java 复制代码
package com.example.fileupload.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;

@FeignClient(name = "file-upload-service", url = "http://localhost:8080/api/files")
public interface FileFeignClient {

    @PostMapping("/upload")
    String uploadFile(@RequestParam("file") MultipartFile file);

    @GetMapping("/download/{filename}")
    byte[] downloadFile(@PathVariable("filename") String filename);
}

Feign 使用示例

在 Service 层,我们可以使用 Feign 客户端来调用远程的文件上传和下载接口。

FileService.java(使用 Feign 上传下载文件)

java 复制代码
package com.example.fileupload.service;

import com.example.fileupload.feign.FileFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Service
public class FileService {

    @Autowired
    private FileFeignClient fileFeignClient;

    // 使用 Feign 上传文件
    public String uploadFileViaFeign(MultipartFile file) {
        return fileFeignClient.uploadFile(file);
    }

    // 使用 Feign 下载文件
    public byte[] downloadFileViaFeign(String filename) {
        return fileFeignClient.downloadFile(filename);
    }
}

总结

本文通过详细的代码示例,展示了如何将 MinIO 集成到 Spring Boot 中,来实现文件的上传和下载。通过 Feign 客户端的远程调用,可以将文件上传和下载的请求发送到远程服务中进行处理。

参考资料

Spring Boot 官方文档

Spring Cloud Feign 官方文档

相关推荐
星迹日1 小时前
数据结构:包装类和泛型
java·开发语言·数据结构·笔记·泛型·通配符·包装类
重生之绝世牛码3 小时前
Java设计模式 —— 【行为型模式】命令模式(Command Pattern) 详解
java·大数据·开发语言·设计模式·命令模式·设计原则
荆州克莱5 小时前
redis学习笔记(一)了解redis
spring boot·spring·spring cloud·css3·技术
杨荧5 小时前
【开源免费】基于Vue和SpringBoot的贸易行业crm系统(附论文)
前端·javascript·jvm·vue.js·spring boot·spring cloud·开源
java排坑日记5 小时前
poi-tl+kkviewfile实现生成pdf业务报告
java·pdf·word
V+zmm101346 小时前
校园约拍微信小程序设计与实现ssm+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm
猿来入此小猿6 小时前
基于SpringBoot小说平台系统功能实现四
java·spring boot·毕业设计·毕业源码·在线小说阅读·在线小说平台·免费学习:猿来入此
追逐时光者7 小时前
.NET集成IdGenerator生成分布式ID
后端·.net
SyntaxSage7 小时前
Scala语言的数据库交互
开发语言·后端·golang
Cosmoshhhyyy7 小时前
LeetCode:2274. 不含特殊楼层的最大连续楼层数(排序 Java)
java·算法·leetcode