最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot集成MinIO實(shí)現(xiàn)分布式文件存儲(chǔ)與管理方式

 更新時(shí)間:2025年09月07日 14:53:46   作者:LOVE_DDZ  
這篇文章主要介紹了SpringBoot集成MinIO實(shí)現(xiàn)分布式文件存儲(chǔ)與管理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一、MinIO 簡(jiǎn)介

MinIO 是一個(gè)高性能的分布式對(duì)象存儲(chǔ)服務(wù)器,兼容 Amazon S3 API。它具有以下特點(diǎn):

  • 輕量級(jí)且易于部署
  • 高性能(讀寫(xiě)速度可達(dá)每秒數(shù)GB)
  • 支持?jǐn)?shù)據(jù)加密和訪問(wèn)控制
  • 提供多種語(yǔ)言的SDK
  • 開(kāi)源且社區(qū)活躍

二、Spring Boot 集成 MinIO

1. 添加依賴

pom.xml 中添加 MinIO Java SDK 依賴:

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

2. 配置 MinIO 連接

application.yml 中配置:

minio:
  endpoint: http://localhost:9000
  accessKey: minioadmin
  secretKey: minioadmin
  bucketName: default-bucket
  secure: false

3. 創(chuàng)建配置類(lèi)

@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();
    }
}

三、實(shí)現(xiàn)文件服務(wù)

@Service
@RequiredArgsConstructor
public class MinioService {

    private final MinioClient minioClient;

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

    /**
     * 檢查存儲(chǔ)桶是否存在
     *
     * @param bucketName 存儲(chǔ)桶名稱
     * @return 存儲(chǔ)桶是否存在 狀態(tài)碼 true:存在 false:不存在
     */
    public boolean bucketExists(String bucketName) throws Exception {
        return !minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 創(chuàng)建存儲(chǔ)桶
     */
    public void makeBucket(String bucketName) throws Exception {
        if (bucketExists(bucketName)) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 列出所有存儲(chǔ)桶
     */
    public List<Bucket> listBuckets() throws Exception {
        return minioClient.listBuckets();
    }

    /**
     * 上傳文件
     *
     * @param file       文件
     * @param bucketName 存儲(chǔ)桶名稱
     * @param rename     是否重命名
     */
    public String uploadFile(MultipartFile file, String bucketName, boolean rename) throws Exception {
        // 如果未指定bucketName,使用默認(rèn)的
        bucketName = getBucketName(bucketName);

        // 檢查存儲(chǔ)桶是否存在,不存在則創(chuàng)建
        ensureBucketExists(bucketName);

        // 生成唯一文件名
        String objectName = generateObjectName(file, rename);
        // 上傳文件
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build());
        return objectName;
    }


    /**
     * 下載文件
     */
    public InputStream downloadFile(String objectName, String bucketName) throws Exception {
        return minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(getBucketName(bucketName))
                        .object(objectName)
                        .build());
    }

    /**
     * 刪除文件
     */
    public void removeFile(String objectName, String bucketName) throws Exception {
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(getBucketName(bucketName))
                        .object(objectName)
                        .build());
    }

    /**
     * 獲取文件URL(先檢查文件是否存在)
     */
    public String getFileUrl(String objectName, String bucketName) throws Exception {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(getBucketName(bucketName))
                            .object(objectName)
                            .build());
        } catch (ErrorResponseException e) {
            // 文件不存在時(shí)拋出異常
            throw new FileNotFoundException("File not found: " + objectName);
        }

        // 文件存在,生成URL
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(getBucketName(bucketName))
                        .object(objectName)
                        .build());
    }

    /**
     * 生成唯一文件名
     */
    private static @Nullable String generateObjectName(MultipartFile file, boolean rename) {
        String fileName = file.getOriginalFilename();
        String objectName = fileName;
        if (rename && fileName != null) {
            objectName = UUID.randomUUID().toString().replaceAll("-", "")
                    + fileName.substring(fileName.lastIndexOf("."));
        }
        return objectName;
    }

    /**
     * 檢查存儲(chǔ)桶是否存在,不存在則創(chuàng)建
     */
    private void ensureBucketExists(String bucketName) throws Exception {
        if (bucketExists(bucketName)) {
            makeBucket(bucketName);
        }
    }

    /**
     * 獲取存儲(chǔ)桶名稱
     */
    private String getBucketName(String bucketName) {
        if (bucketName == null || bucketName.isEmpty()) {
            bucketName = this.bucketName;
        }
        return bucketName;
    }
}

四、REST API 實(shí)現(xiàn)

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/file")
public class MinioController {

    private final MinioService minioService;

    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file,
                                             @RequestParam(value = "bucketName", required = false) String bucketName) {
        try {
            String objectName = minioService.uploadFile(file, bucketName, false);
            return ResponseEntity.ok(objectName);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
    }

    @GetMapping("/download")
    public ResponseEntity<byte[]> downloadFile(@RequestParam String objectName,
                                               @RequestParam(value = "bucketName", required = false) String bucketName) {
        try {
            InputStream stream = minioService.downloadFile(objectName, bucketName);
            byte[] bytes = stream.readAllBytes();

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData("attachment", objectName);

            return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
        }
    }

    @DeleteMapping("/delete")
    public ResponseEntity<String> deleteFile(@RequestParam String objectName,
                                             @RequestParam(value = "bucketName", required = false) String bucketName) {
        try {
            minioService.removeFile(objectName, bucketName);
            return ResponseEntity.ok("File deleted successfully");
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
    }

    @GetMapping("/url")
    public ResponseEntity<String> getFileUrl(@RequestParam String objectName,
                                             @RequestParam(value = "bucketName", required = false) String bucketName) {
        try {
            String url = minioService.getFileUrl(objectName, bucketName);
            return ResponseEntity.ok(url);
        } catch (FileNotFoundException e) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
    }
}

五、高級(jí)功能實(shí)現(xiàn)

1. 分片上傳

public String multipartUpload(MultipartFile file, String bucketName) {
    // 1. 初始化分片上傳
    String uploadId = minioClient.initiateMultipartUpload(...);
    
    // 2. 分片上傳
    Map<Integer, String> etags = new HashMap<>();
    for (int partNumber = 1; partNumber <= totalParts; partNumber++) {
        PartSource partSource = getPartSource(file, partNumber);
        String etag = minioClient.uploadPart(...);
        etags.put(partNumber, etag);
    }
    
    // 3. 完成分片上傳
    minioClient.completeMultipartUpload(...);
    
    return objectName;
}

2. 文件預(yù)覽

    @GetMapping("/preview/{objectName}")
    public ResponseEntity<Resource> previewFile(
            @PathVariable String objectName,
            @RequestParam(value = "bucketName", required = false) String bucketName) throws Exception {

        String contentType = minioService.getFileContentType(objectName, bucketName);
        InputStream inputStream = minioService.downloadFile(objectName, bucketName);

        return ResponseEntity.ok()
                .contentType(MediaType.parseMediaType(contentType))
                .body(new InputStreamResource(inputStream));
    }

六、最佳實(shí)踐

安全性考慮

  • 為預(yù)簽名URL設(shè)置合理的過(guò)期時(shí)間
  • 實(shí)現(xiàn)細(xì)粒度的訪問(wèn)控制
  • 對(duì)上傳文件進(jìn)行病毒掃描

性能優(yōu)化

  • 使用CDN加速文件訪問(wèn)
  • 對(duì)大文件使用分片上傳
  • 實(shí)現(xiàn)客戶端直傳(Presigned URL)

監(jiān)控與日志

  • 記錄所有文件操作
  • 監(jiān)控存儲(chǔ)空間使用情況
  • 設(shè)置自動(dòng)清理策略

七、常見(jiàn)問(wèn)題解決

連接超時(shí)問(wèn)題

@Bean
public MinioClient minioClient() {
    return MinioClient.builder()
            .endpoint(endpoint)
            .credentials(accessKey, secretKey)
            .httpClient(HttpClient.newBuilder()
                    .connectTimeout(Duration.ofSeconds(30))
                    .build())
            .build();
}

文件存在性檢查優(yōu)化

public boolean fileExists(String objectName, String bucketName) {
    try {
        minioClient.statObject(
            StatObjectArgs.builder()
                .bucket(getBucketName(bucketName))
                .object(objectName)
                .build());
        return true;
    } catch (ErrorResponseException e) {
        if (e.errorResponse().code().equals("NoSuchKey")) {
            return false;
        }
        throw new FileStorageException("檢查文件存在性失敗", e);
    } catch (Exception e) {
        throw new FileStorageException("檢查文件存在性失敗", e);
    }
}

八、總結(jié)

通過(guò)本文的介紹,我們實(shí)現(xiàn)了:

  1. Spring Boot 與 MinIO 的基本集成
  2. 文件上傳、下載、刪除等基礎(chǔ)功能
  3. 文件預(yù)覽、分片上傳等高級(jí)功能
  4. 安全性、性能等方面的最佳實(shí)踐

MinIO 作為輕量級(jí)的對(duì)象存儲(chǔ)解決方案,非常適合中小型項(xiàng)目使用。結(jié)合 Spring Boot 可以快速構(gòu)建強(qiáng)大的文件存儲(chǔ)服務(wù)。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • MyBatis配置的應(yīng)用與對(duì)比jdbc的優(yōu)勢(shì)

    MyBatis配置的應(yīng)用與對(duì)比jdbc的優(yōu)勢(shì)

    這篇文章主要介紹了MyBatis配置的使用與相對(duì)于jdbc的優(yōu)勢(shì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Java批量插入數(shù)據(jù)的代碼實(shí)現(xiàn)

    Java批量插入數(shù)據(jù)的代碼實(shí)現(xiàn)

    日常工作或者學(xué)習(xí)中,可能會(huì)遇到批量插入數(shù)據(jù)的需求,一般情況下數(shù)據(jù)量少的時(shí)候,我們會(huì)直接調(diào)用批量接口插入數(shù)據(jù)即可,當(dāng)數(shù)據(jù)量特別大時(shí),我們就會(huì)用到分批插入數(shù)據(jù),所以本文給大家介紹了Java批量插入數(shù)據(jù)的代碼實(shí)現(xiàn),需要的朋友可以參考下
    2024-01-01
  • SpringBoot整合EasyCaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能

    SpringBoot整合EasyCaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能

    這篇文章主要介紹了SpringBoot整合EasyCaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-02-02
  • java中List刪除時(shí)需要的注意事項(xiàng)

    java中List刪除時(shí)需要的注意事項(xiàng)

    最近在利用java中的LIST在刪除時(shí)發(fā)現(xiàn)了一個(gè)錯(cuò)我,通過(guò)查找相關(guān)的資料終于解決了,覺(jué)著有必要分享處理給同樣遇到這個(gè)問(wèn)題的朋友參考,下面這篇文章主要介紹了java中List刪除時(shí)需要的注意事項(xiàng),需要的朋友可以一起來(lái)看看吧。
    2017-01-01
  • AgentScope Java 集成 Spring AI Alibaba Workflow 完整指南

    AgentScope Java 集成 Spring AI Alibab

    本文提出了一個(gè)基于AgentScope、SpringAI-Alibaba和Java2AI生態(tài)GraphCore的集成方案,涵蓋核心思路、工程配置、代碼實(shí)現(xiàn)和最佳實(shí)踐,感興趣的朋友跟隨小編一起看看吧
    2026-04-04
  • Mybatis 入門(mén)示例代碼之 Association

    Mybatis 入門(mén)示例代碼之 Association

    這篇文章主要介紹了Mybatis 入門(mén)示例代碼之 Association,需要的的朋友參考下
    2017-02-02
  • java面試題解LeetCode27二叉樹(shù)的鏡像實(shí)例

    java面試題解LeetCode27二叉樹(shù)的鏡像實(shí)例

    這篇文章主要為大家介紹了java面試題解LeetCode27二叉樹(shù)的鏡像實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • Java實(shí)現(xiàn)String轉(zhuǎn)Integer的四種方案對(duì)比

    Java實(shí)現(xiàn)String轉(zhuǎn)Integer的四種方案對(duì)比

    在Java項(xiàng)目開(kāi)發(fā)中,經(jīng)常會(huì)有類(lèi)型轉(zhuǎn)換處理,這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)String轉(zhuǎn)Integer的四種方案對(duì)比,感興趣的小伙伴可以了解下
    2026-03-03
  • java 中Spark中將對(duì)象序列化存儲(chǔ)到hdfs

    java 中Spark中將對(duì)象序列化存儲(chǔ)到hdfs

    這篇文章主要介紹了java 中Spark中將對(duì)象序列化存儲(chǔ)到hdfs的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • idea如何配置springboot熱部署

    idea如何配置springboot熱部署

    這篇文章主要介紹了idea如何配置springboot熱部署問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12

最新評(píng)論

加查县| 菏泽市| 墨竹工卡县| 承德县| 漠河县| 林西县| 武夷山市| 大方县| 都安| 石门县| 蒙山县| 大连市| 云梦县| 璧山县| 邢台市| 宜兰市| 德令哈市| 乌兰浩特市| 柳州市| 喀喇沁旗| 玛多县| 咸阳市| 斗六市| 新余市| 错那县| 湘西| 杨浦区| 民乐县| 双鸭山市| 陕西省| 车险| 石城县| 普格县| 个旧市| 屯门区| 景德镇市| 板桥市| 牙克石市| 丽水市| 桐梓县| 兴仁县|