SpringBoot 整合FastDFS/MinIO分布式文件系統


一、FastDFS

  1. Maven依賴

<dependency>
    <groupId>com.github.tobato</groupId>
    <artifactId>fastdfs-client</artifactId>
    <version>1.26.5</version>
</dependency>

  2. 配置application.yml文件

fdfs:
  # 鏈接超時
  connect-timeout: 60
  # 讀取時間
  so-timeout: 60
  # 生成縮略圖參數
  thumb-image:
    width: 150
    height: 150
  tracker-list: 127.0.0.1:22122

  3. FastDFS配置類

@Configuration
@Import(FdfsClientConfig.class)
// Jmx重復注冊bean的問題
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
public class DfsConfig {
}

   可參考:https://blog.51cto.com/14439672/2434896

 

二、MinIO

  簡介:MinIO是一個非常輕量的對象存儲服務,非常適合存儲非結構化的數據,比如圖片、視頻、日志文件、容器鏡像等,文件最大是5TB,這對一般的文件存儲足夠了,而且功能強大,還提供了可視化界面。

  1. pom.xml Maven依賴

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

  2. application.yml 配置文件

minio:
    endpoint: http://localhost:9000
    access-key: minio
    secret-key: minio

   3. 配置類

復制代碼
package com.ruhuanxingyun.minio.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @description: Minio文件服務參數
 * @author: ruphie
 * @date: Create in 2020/8/26 22:03
 * @company: ruhuanxingyun
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {

    /**
     * 對象存儲服務的URL
     */
    private String endpoint;

    /**
     * 用戶ID,可以唯一標識的賬戶
     */
    private String accessKey;

    /**
     * 賬戶的密碼
     */
    private String secretKey;

}
復制代碼

  4. 配置類

復制代碼
package com.ruhuanxingyun.minio.config;

import com.ruhuanxingyun.minio.common.Constants;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @description: Minio客戶端
 * @author: ruphie
 * @date: Create in 2020/8/26 22:08
 * @company: ruhuanxingyun
 */
@Configuration
public class MinioClientConfig {

    @Autowired
    private MinioProperties minioProperties;

    @Bean
    public MinioClient minioClient() {
        MinioClient.Builder builder = MinioClient.builder();
        MinioClient minioClient = builder.endpoint(minioProperties.getEndpoint())
                .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
                .build();

        try {
            for (String bucket : Constants.BUCKETS) {
                BucketExistsArgs args = BucketExistsArgs.builder()
                        .bucket(bucket)
                        .build();
                // 檢查存儲桶是否存在
                if (!minioClient.bucketExists(args)) {
                    MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder()
                            .bucket(bucket)
                            .build();
                    // 創建一個新的存儲桶
                    minioClient.makeBucket(makeBucketArgs);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return minioClient;
    }

}
復制代碼

  5. 文件服務類

復制代碼
package com.ruhuanxingyun.minio.service;

import cn.hutool.core.util.StrUtil;
import com.ruhuanxingyun.minio.common.Constants;
import com.ruhuanxingyun.minio.util.FileUtils;
import io.minio.MinioClient;
import io.minio.ObjectWriteArgs;
import io.minio.PutObjectArgs;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;

/**
 * @description: 上傳文件 服務層
 * @author: ruphie
 * @date: Create in 2020/8/26 22:15
 * @company: ruhuanxingyun
 */
@Service
public class FileUploadService {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 上傳文件
     *
     * @param file    文件
     * @param fileMd5 文件MD5值
     * @return 上傳結果
     */
    public String upload(MultipartFile file, String fileMd5) {
        String md5 = FileUtils.getFileMd5(file);
        // 前端兼容MD5值大小寫,設備端傳遞小寫
        if (!StrUtil.equalsIgnoreCase(fileMd5, md5)) {
            return "上傳文件MD5值不匹配!";
        }

        InputStream is = null;
        try {
            is = file.getInputStream();
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(Constants.BUCKETS[0])
                    .object(file.getOriginalFilename())
                    .stream(is, file.getSize(), ObjectWriteArgs.MAX_PART_SIZE)
                    .contentType(file.getContentType())
                    .build();
            // 通過InputStream上傳對象
            minioClient.putObject(putObjectArgs);
        } catch (Exception e) {
            e.printStackTrace();

            return "文件上傳失敗!";
        } finally {
            IOUtils.closeQuietly(is);
        }

        return "文件上傳成功!";
    }

    /**
     * 重置文件上傳進度
     *
     * @param uuid 唯一標識符
     */
    public void reset(String uuid) {
        String key = String.format("%s:%s", Constants.FILE_UPLOAD_PROGRESS, uuid);
        stringRedisTemplate.delete(key);
    }

    /**
     * 文件上傳進度
     *
     * @param uuid 唯一標識符
     * @return 進度
     */
    public int progress(String uuid) {
        String key = String.format("%s:%s", Constants.FILE_UPLOAD_PROGRESS, uuid);
        String percent = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotEmpty(percent)) {
            // 上傳完全
            if (StrUtil.equals("100", percent)) {
                stringRedisTemplate.delete(key);
            }

            return Integer.parseInt(percent);
        }

        return 0;
    }

}
復制代碼
復制代碼
package com.ruhuanxingyun.minio.service;

import com.ruhuanxingyun.minio.common.Constants;
import com.ruhuanxingyun.minio.util.FileUtils;
import io.minio.*;
import io.minio.http.Method;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;

/**
 * @description: 下載文件 服務層
 * @author: ruphie
 * @date: Create in 2020/12/8 21:46
 * @company: ruhuanxingyun
 */
@Service
public class FileDownloadService {

    @Autowired
    private MinioClient minioClient;

    /**
     * 下載文件URL
     *
     * @param filePath 文件路徑
     * @return URL
     */
    public String downloadUrl(String filePath) {
        String bucketName = Constants.BUCKETS[0];
        String objectName = FileUtils.getObjectName(filePath, bucketName);
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(bucketName)
                .object(objectName)
                .expiry(GetPresignedObjectUrlArgs.DEFAULT_EXPIRY_TIME)
                .build();

        try {
            return minioClient.getPresignedObjectUrl(args);
        } catch (Exception e) {
            e.printStackTrace();

            return null;
        }
    }

    /**
     * 下載文件
     *
     * @param filePath 文件路徑
     * @param response 響應流
     */
    public void download(String filePath, HttpServletResponse response) {
        String bucketName = Constants.BUCKETS[0];
        String objectName = FileUtils.getObjectName(filePath, bucketName);
        StatObjectArgs args = StatObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build();
        GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build();

        InputStream is = null;
        BufferedInputStream ins = null;
        BufferedOutputStream bos = null;
        try {
            // 獲取對象的元數據
            ObjectStat objectStat = minioClient.statObject(args);
            is = minioClient.getObject(getObjectArgs);

            response.setHeader(HttpHeaders.CACHE_CONTROL, "no-store");
            response.setHeader(HttpHeaders.PRAGMA, "no-cache");
            response.setDateHeader(HttpHeaders.EXPIRES, 0);
            response.setContentType(objectStat.contentType());
            response.setContentLengthLong(objectStat.length());
            response.setHeader("Content-Disposition", "attachment;filename=" + objectStat.name());

            ins = new BufferedInputStream(is);
            bos = new BufferedOutputStream(response.getOutputStream());
            byte[] buffer = new byte[1024 * 1024];
            int bytesRead = -1;
            while ((bytesRead = ins.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }

            bos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(bos);
            IOUtils.closeQuietly(ins);
            IOUtils.closeQuietly(is);
        }
    }
}
復制代碼
復制代碼
package com.ruhuanxingyun.minio.service;

import com.ruhuanxingyun.minio.common.Constants;
import com.ruhuanxingyun.minio.util.FileUtils;
import io.minio.MinioClient;
import io.minio.RemoveObjectArgs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @description: 刪除文件 服務層
 * @author: ruphie
 * @date: Create in 2020/12/8 21:49
 * @company: ruhuanxingyun
 */
@Service
public class FileDeleteService {

    @Autowired
    private MinioClient minioClient;

    /**
     * 刪除文件
     *
     * @param filePath 文件路徑
     * @return 刪除狀態
     */
    public String delete(String filePath) {
        String bucketName = Constants.BUCKETS[0];
        String objectName = FileUtils.getObjectName(filePath, bucketName);
        RemoveObjectArgs args = RemoveObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build();

        try {
            minioClient.removeObject(args);

            return "刪除文件成功!";
        } catch (Exception e) {
            e.printStackTrace();

            return "刪除文件失敗!";
        }
    }

}
復制代碼
復制代碼
package com.ruhuanxingyun.minio.util;

import org.springframework.web.multipart.MultipartFile;

import java.math.BigInteger;
import java.security.MessageDigest;

/**
 * @description: 文件工具類
 * @author: ruphie
 * @date: Create in 2020/12/8 22:25
 * @company: ruhuanxingyun
 */
public class FileUtils {

    /**
     * 獲取對象名
     *
     * @param filePath 文件路徑
     * @param bucket   存儲桶
     * @return 對象名
     */
    public static String getObjectName(String filePath, String bucket) {
        return filePath.replace(String.format("%s/", bucket), "");
    }

    /**
     * 獲取文件MD5值
     *
     * @param file 文件
     * @return MD5
     */
    public static String getFileMd5(MultipartFile file) {
        try {
            byte[] bytes = file.getBytes();
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            byte[] digest = messageDigest.digest(bytes);

            return new BigInteger(1, digest).toString(16);
        } catch (Exception e) {
            e.printStackTrace();

            return null;
        }
    }

}
復制代碼

   6. 斷點續傳

 

  可參考:MinIO官方文檔

      SpringBoot通過Minio實現大文件分片上傳

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM