动力节点-MinIO分布式存储从0到Vue+SpringBoot整合开发

47 阅读2分钟

MinIO分布式存储与Vue+SpringBoot全栈整合开发指南(含代码实现)

一、MinIO分布式存储的部署与配置

1. MinIO单机版安装与配置

安装步骤

  1. 下载MinIO二进制文件(Linux系统为例):
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
  1. 创建数据存储目录并启动服务:
mkdir -p ~/minio/data
./minio server ~/minio/data --console-address ":9001"
  1. 访问Web管理界面(默认端口9001):
http://your-server-ip:9001

2. MinIO分布式集群部署

4节点集群示例配置

export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=your-strong-password

minio server http://node{1...4}/mnt/data{1...4} \
  --console-address ":9001"

关键配置说明

  • 每个节点至少需要4块磁盘
  • 数据目录建议使用XFS文件系统
  • 生产环境建议配置TLS证书

二、SpringBoot后端集成MinIO

1. 添加依赖配置

pom.xml依赖

<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.5.2</version>
</dependency>

application.yml配置

minio:
  endpoint: http://your-minio-server:9000
  accessKey: your-access-key
  secretKey: your-secret-key
  bucket: default-bucket

2. 核心服务实现

MinIO配置类

@Configuration
public class MinioConfig {
    @Value("${minio.endpoint}")
    private String endpoint;
    
    @Value("${minio.accessKey}")
    private String accessKey;
    
    @Value("${minio.secretKey}")
    private String secretKey;
    
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }
}

文件上传服务

@Service
public class FileStorageService {
    @Autowired
    private MinioClient minioClient;
    
    @Value("${minio.bucket}")
    private String bucketName;
    
    public String uploadFile(MultipartFile file) throws Exception {
        String objectName = UUID.randomUUID() + "-" + file.getOriginalFilename();
        
        minioClient.putObject(
            PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(file.getContentType())
                .build());
        
        return objectName;
    }
}

三、Vue前端实现文件管理

1. 文件上传组件实现

FileUpload.vue

<template>
  <div>
    <input type="file" @change="handleFileChange" />
    <button @click="uploadFile">上传</button>
    <progress v-if="uploading" :value="progress" max="100"></progress>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      selectedFile: null,
      uploading: false,
      progress: 0
    };
  },
  methods: {
    handleFileChange(event) {
      this.selectedFile = event.target.files[0];
    },
    async uploadFile() {
      if (!this.selectedFile) return;
      
      const formData = new FormData();
      formData.append('file', this.selectedFile);
      
      try {
        this.uploading = true;
        const response = await axios.post('/api/files/upload', formData, {
          onUploadProgress: progressEvent => {
            this.progress = Math.round(
              (progressEvent.loaded * 100) / progressEvent.total
            );
          }
        });
        this.$emit('upload-success', response.data);
      } catch (error) {
        console.error('上传失败:', error);
      } finally {
        this.uploading = false;
      }
    }
  }
};
</script>

2. 文件列表展示组件

FileList.vue

<template>
  <div>
    <table>
      <thead>
        <tr>
          <th>文件名</th>
          <th>大小</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="file in files" :key="file.name">
          <td>{{ file.name }}</td>
          <td>{{ formatFileSize(file.size) }}</td>
          <td>
            <button @click="downloadFile(file.name)">下载</button>
            <button @click="previewFile(file.name)">预览</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  props: {
    files: Array
  },
  methods: {
    formatFileSize(bytes) {
      if (bytes === 0) return '0 Bytes';
      const k = 1024;
      const sizes = ['Bytes', 'KB', 'MB', 'GB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    },
    downloadFile(filename) {
      window.open(`/api/files/download/${filename}`, '_blank');
    },
    previewFile(filename) {
      this.$emit('preview', filename);
    }
  }
};
</script>

四、高级功能实现

1. 大文件分片上传(后端)

public String uploadLargeFile(MultipartFile file, int chunkNumber, 
    int totalChunks, String fileId) throws Exception {
    
    String tempDir = System.getProperty("java.io.tmpdir");
    Path tempPath = Paths.get(tempDir, fileId);
    
    // 写入分片
    Files.write(tempPath.resolve(chunkNumber + ".part"), 
        file.getBytes(), StandardOpenOption.CREATE);
    
    // 检查是否所有分片都已上传
    if (Files.list(tempPath).count() == totalChunks) {
        // 合并文件
        Path mergedFile = mergeFiles(tempPath, fileId, file.getOriginalFilename());
        
        // 上传到MinIO
        String objectName = fileId + "-" + file.getOriginalFilename();
        minioClient.putObject(
            PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .stream(Files.newInputStream(mergedFile), 
                    Files.size(mergedFile), -1)
                .build());
        
        // 清理临时文件
        FileUtils.deleteDirectory(tempPath.toFile());
        
        return objectName;
    }
    
    return "chunk_uploaded";
}

2. 文件预览服务

@GetMapping("/preview/{objectName}")
public ResponseEntity<Resource> previewFile(
    @PathVariable String objectName) throws Exception {
    
    InputStream stream = minioClient.getObject(
        GetObjectArgs.builder()
            .bucket(bucketName)
            .object(objectName)
            .build());
    
    String contentType = minioClient.statObject(
        StatObjectArgs.builder()
            .bucket(bucketName)
            .object(objectName)
            .build()).contentType();
    
    return ResponseEntity.ok()
        .contentType(MediaType.parseMediaType(contentType))
        .header(HttpHeaders.CONTENT_DISPOSITION, 
            "inline; filename=\"" + objectName + "\"")
        .body(new InputStreamResource(stream));
}

五、安全与权限控制

1. 存储桶策略配置

public void setBucketPolicy(String bucketName) throws Exception {
    String policyJson = """
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {"AWS": ["*"]},
          "Action": ["s3:GetObject"],
          "Resource": ["arn:aws:s3:::%s/*"],
          "Condition": {
            "IpAddress": {"aws:SourceIp": ["192.168.1.0/24"]}
          }
        }
      ]
    }
    """.formatted(bucketName);
    
    minioClient.setBucketPolicy(
        SetBucketPolicyArgs.builder()
            .bucket(bucketName)
            .config(policyJson)
            .build());
}

2. 临时访问URL生成

public String generatePresignedUrl(String objectName, 
    int expiryDuration, TimeUnit timeUnit) throws Exception {
    
    return minioClient.getPresignedObjectUrl(
        GetPresignedObjectUrlArgs.builder()
            .method(Method.GET)
            .bucket(bucketName)
            .object(objectName)
            .expiry(expiryDuration, timeUnit)
            .build());
}

以上代码实现展示了MinIO与Vue+SpringBoot全栈整合的核心功能,包括基础文件上传下载、大文件分片处理、文件预览以及安全控制等关键功能模块。实际开发中可根据业务需求进行扩展和优化。