前端开发入门到精通的在线学习网站

网站首页 > 资源文章 正文

SpringBoot+MinIO 对象存储实战:从零开始实现文件上传与下载

qiguaw 2025-03-20 18:19:29 资源文章 12 ℃ 0 评论

Spring Boot 与 MinIO 对象存储实战:从零开始实现文件上传与下载

引言

MinIO 是一个高性能、分布式对象存储系统,兼容 Amazon S3 API。它非常适合存储非结构化数据,如图片、视频、日志文件等。MinIO 的安装和配置非常简单,且支持分布式部署,能够轻松应对大规模数据存储的需求。。

目录

  1. MinIO 简介与安装
  2. Spring Boot 项目搭建
  3. MinIO 客户端配置
  4. 文件上传功能实现
  5. 文件下载功能实现
  6. 文件删除功能实现
  7. 文件列表查询功能实现
  8. 异常处理与日志记录
  9. 前端页面设计与实现
  10. 测试与部署
  11. 总结与展望

1. MinIO 简介与安装

1.1 MinIO 简介

MinIO 是一个开源的对象存储服务器,兼容 Amazon S3 API。它专为云原生应用程序设计,具有高性能、可扩展性和易用性。MinIO 可以部署在本地、私有云或公有云环境中,支持分布式部署,能够轻松应对大规模数据存储的需求。

MinIO 的主要特点包括:

  • 高性能:MinIO 使用 Golang 编写,具有极高的性能,能够处理大量的并发请求。
  • 分布式:MinIO 支持分布式部署,能够轻松扩展存储容量和性能。
  • 兼容 S3 API:MinIO 完全兼容 Amazon S3 API,可以无缝集成现有的 S3 客户端和工具。
  • 易用性:MinIO 的安装和配置非常简单,几分钟内即可搭建一个对象存储服务。

1.2 MinIO 安装

MinIO 的安装非常简单,支持多种操作系统和部署方式。以下是使用 Docker 安装 MinIO 的步骤。

1.2.1 安装 Docker

如果你还没有安装 Docker,可以参考 Docker 官方文档 进行安装。

1.2.2 使用 Docker 安装 MinIO

在终端中运行以下命令,使用 Docker 启动一个 MinIO 实例:

docker run -p 9000:9000 -p 9001:9001 \
  --name minio \
  -v /mnt/data:/data \
  -e "MINIO_ROOT_USER=minioadmin" \
  -e "MINIO_ROOT_PASSWORD=minioadmin" \
  minio/minio server /data --console-address ":9001"
  • -p 9000:9000:将 MinIO 服务的 9000 端口映射到主机的 9000 端口。
  • -p 9001:9001:将 MinIO 控制台的 9001 端口映射到主机的 9001 端口。
  • -v /mnt/data:/data:将主机的 /mnt/data 目录挂载到容器的 /data 目录,用于持久化存储。
  • -e "MINIO_ROOT_USER=minioadmin":设置 MinIO 的 root 用户名为 minioadmin。
  • -e "MINIO_ROOT_PASSWORD=minioadmin":设置 MinIO 的 root 用户密码为 minioadmin。
  • minio/minio server /data --console-address ":9001":启动 MinIO 服务,并将控制台地址设置为 :9001。

1.2.3 访问 MinIO 控制台

在浏览器中访问 http://localhost:9001,使用 minioadmin 作为用户名和密码登录 MinIO 控制台。

2. Spring Boot 项目搭建

2.1 创建 Spring Boot 项目

使用 Spring Initializr 创建一个新的 Spring Boot 项目。选择以下依赖:

  • Spring Web:用于构建 Web 应用程序。
  • Spring Boot DevTools:用于开发时的热部署。
  • Lombok:用于简化代码编写。

2.2 项目结构

项目结构如下:

src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           └── miniodemo
│   │               ├── controller
│   │               ├── service
│   │               ├── config
│   │               ├── exception
│   │               └── MinioDemoApplication.java
│   └── resources
│       ├── static
│       ├── templates
│       └── application.properties
└── test
    └── java
        └── com
            └── example
                └── miniodemo

2.3 配置application.properties

在 application.properties 中添加以下配置:

# MinIO 配置
minio.endpoint=http://localhost:9000
minio.access-key=minioadmin
minio.secret-key=minioadmin
minio.bucket-name=my-bucket

3. MinIO 客户端配置

3.1 添加 MinIO 客户端依赖

在 pom.xml 中添加 MinIO 客户端的依赖:


    io.minio
    minio
    8.3.0

3.2 配置 MinIO 客户端

创建一个 MinioConfig 类,用于配置 MinIO 客户端:

package com.example.miniodemo.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.endpoint}")
    private String endpoint;

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

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

    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }
}

4. 文件上传功能实现

4.1 创建FileService服务类

创建一个 FileService 类,用于处理文件上传、下载、删除等操作:

package com.example.miniodemo.service;

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

import java.io.InputStream;
import java.util.UUID;

@Service
public class FileService {

    @Autowired
    private MinioClient minioClient;

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

    public String uploadFile(MultipartFile file) throws Exception {
        try {
            // 生成唯一的文件名
            String fileName = UUID.randomUUID().toString() + "-" + file.getOriginalFilename();

            // 获取文件的输入流
            InputStream inputStream = file.getInputStream();

            // 上传文件到 MinIO
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .stream(inputStream, file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build());

            // 返回文件的访问 URL
            return minioClient.getObjectUrl(bucketName, fileName);
        } catch (MinioException e) {
            throw new Exception("文件上传失败: " + e.getMessage());
        }
    }
}

4.2 创建FileController控制器类

创建一个 FileController 类,用于处理文件上传的 HTTP 请求:

package com.example.miniodemo.controller;

import com.example.miniodemo.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
public class FileController {

    @Autowired
    private FileService fileService;

    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            return fileService.uploadFile(file);
        } catch (Exception e) {
            return "文件上传失败: " + e.getMessage();
        }
    }
}

4.3 测试文件上传功能

启动 Spring Boot 应用程序,使用 Postman 或 curl 工具测试文件上传功能:

curl -X POST -F "file=@/path/to/your/file.jpg" http://localhost:8080/upload

如果上传成功,将返回文件的访问 URL。

5. 文件下载功能实现

5.1 在FileService中添加下载功能

在 FileService 类中添加下载文件的方法:

public InputStream downloadFile(String fileName) throws Exception {
    try {
        return minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .build());
    } catch (MinioException e) {
        throw new Exception("文件下载失败: " + e.getMessage());
    }
}

5.2 在FileController中添加下载接口

在 FileController 类中添加下载文件的接口:

@GetMapping("/download/{fileName}")
public ResponseEntity downloadFile(@PathVariable String fileName) {
    try {
        InputStream inputStream = fileService.downloadFile(fileName);
        InputStreamResource resource = new InputStreamResource(inputStream);

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(resource);
    } catch (Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
    }
}

5.3 测试文件下载功能

启动 Spring Boot 应用程序,使用浏览器或 curl 工具测试文件下载功能:

curl -O http://localhost:8080/download/your-file-name.jpg

如果下载成功,文件将被保存到当前目录。

6. 文件删除功能实现

6.1 在FileService中添加删除功能

在 FileService 类中添加删除文件的方法:

public void deleteFile(String fileName) throws Exception {
    try {
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .build());
    } catch (MinioException e) {
        throw new Exception("文件删除失败: " + e.getMessage());
    }
}

6.2 在FileController中添加删除接口

在 FileController 类中添加删除文件的接口:

@DeleteMapping("/delete/{fileName}")
public String deleteFile(@PathVariable String fileName) {
    try {
        fileService.deleteFile(fileName);
        return "文件删除成功";
    } catch (Exception e) {
        return "文件删除失败: " + e.getMessage();
    }
}

6.3 测试文件删除功能

启动 Spring Boot 应用程序,使用 Postman 或 curl 工具测试文件删除功能:

curl -X DELETE http://localhost:8080/delete/your-file-name.jpg

如果删除成功,将返回 "文件删除成功"。

7. 文件列表查询功能实现

7.1 在FileService中添加列表查询功能

在 FileService 类中添加查询文件列表的方法:

public List listFiles() throws Exception {
    try {
        List fileNames = new ArrayList<>();
        Iterable<Result> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .build());

        for (Result result : results) {
            fileNames.add(result.get().objectName());
        }

        return fileNames;
    } catch (MinioException e) {
        throw new Exception("文件列表查询失败: " + e.getMessage());
    }
}

7.2 在FileController中添加列表查询接口

在 FileController 类中添加查询文件列表的接口:

@GetMapping("/list")
public List listFiles() {
    try {
        return fileService.listFiles();
    } catch (Exception e) {
        return Collections.singletonList("文件列表查询失败: " + e.getMessage());
    }
}

7.3 测试文件列表查询功能

启动 Spring Boot 应用程序,使用浏览器或 curl 工具测试文件列表查询功能:

curl http://localhost:8080/list

如果查询成功,将返回文件列表。

8. 异常处理与日志记录

8.1 自定义异常类

创建一个自定义异常类 FileStorageException,用于处理文件存储相关的异常:

package com.example.miniodemo.exception;

public class FileStorageException extends RuntimeException {
    public FileStorageException(String message) {
        super(message);
    }

    public FileStorageException(String message, Throwable cause) {
        super(message, cause);
    }
}

8.2 全局异常处理

创建一个全局异常处理类 GlobalExceptionHandler,用于处理应用程序中的异常:

package com.example.miniodemo.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(FileStorageException.class)
    public ResponseEntity handleFileStorageException(FileStorageException e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity handleException(Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("发生未知错误: " + e.getMessage());
    }
}

8.3 日志记录

在 FileService 类中使用 @Slf4j 注解记录日志:

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class FileService {
    // ...

    public String uploadFile(MultipartFile file) throws Exception {
        try {
            // ...
            log.info("文件上传成功: {}", fileName);
            return minioClient.getObjectUrl(bucketName, fileName);
        } catch (MinioException e) {
            log.error("文件上传失败: {}", e.getMessage());
            throw new FileStorageException("文件上传失败: " + e.getMessage());
        }
    }

    // ...
}

9. 前端页面设计与实现

9.1 创建前端页面

在 src/main/resources/static 目录下创建一个 index.html 文件,用于上传和下载文件:




    
    
    文件上传与下载


    

文件上传

文件列表

    <script> document.getElementById('uploadForm').addEventListener('submit', function (e) { e.preventDefault(); const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; const formData = new FormData(); formData.append('file', file); fetch('/upload', { method: 'POST', body: formData }).then(response => response.text()) .then(data => { alert(data); loadFileList(); }); }); function loadFileList() { fetch('/list') .then(response => response.json()) .then(data => { const fileList = document.getElementById('fileList'); fileList.innerHTML = ''; data.forEach(fileName => { const listItem = document.createElement('li'); listItem.innerHTML = `${fileName}`; fileList.appendChild(listItem); }); }); } loadFileList(); </script>

    9.2 测试前端页面

    启动 Spring Boot 应用程序,访问 http://localhost:8080,使用前端页面上传和下载文件。

    10. 测试与部署

    10.1 单元测试

    编写单元测试类 FileServiceTest,测试文件上传、下载、删除和列表查询功能:

    package com.example.miniodemo.service;
    
    import io.minio.MinioClient;
    import io.minio.errors.MinioException;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.InputStream;
    
    import static org.mockito.ArgumentMatchers.any;
    import static org.mockito.Mockito.*;
    
    class FileServiceTest {
    
        @Mock
        private MinioClient minioClient;
    
        @Mock
        private MultipartFile multipartFile;
    
        @InjectMocks
        private FileService fileService;
    
        @BeforeEach
        void setUp() {
            MockitoAnnotations.openMocks(this);
        }
    
        @Test
        void uploadFile() throws Exception {
            when(multipartFile.getInputStream()).thenReturn(mock(InputStream.class));
            when(multipartFile.getSize()).thenReturn(1024L);
            when(multipartFile.getContentType()).thenReturn("image/jpeg");
    
            fileService.uploadFile(multipartFile);
    
            verify(minioClient, times(1)).putObject(any());
        }
    
        // 其他测试方法...
    }
    

    10.2 部署

    将 Spring Boot 应用程序打包为 JAR 文件,并部署到服务器上:

    mvn clean package
    java -jar target/minio-demo-0.0.1-SNAPSHOT.jar
    

    11. 总结与展望

    本文详细介绍了如何使用 Spring Boot 和 MinIO 实现文件的上传、下载、删除和列表查询功能。通过本文的学习,你应该能够掌握 MinIO 的基本用法,并能够在 Spring Boot 项目中集成 MinIO 进行文件存储和管理。

    在实际项目中,你可以根据需求进一步扩展功能,例如:

    • 权限控制:为不同的用户或角色设置不同的文件访问权限。
    • 文件分片上传:支持大文件的分片上传和断点续传。
    • 文件预览:支持图片、视频、文档等文件的在线预览。
    • 分布式部署:将 MinIO 部署为分布式集群,提高存储容量和性能。

    本文暂时没有评论,来添加一个吧(●'◡'●)

    欢迎 发表评论:

    最近发表
    标签列表