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

SpringBoot整合MinIO實(shí)現(xiàn)全場(chǎng)景文件操作管理

 更新時(shí)間:2025年06月03日 09:54:00   作者:天天摸魚(yú)的java工程師  
這篇文章主要為大家介紹了SpringBoot 整合 MinIO 的全過(guò)程,從為什么選擇 MinIO,到各種文件操作的實(shí)現(xiàn),包括簡(jiǎn)單的文件上傳,批量上傳,文件下載,文件預(yù)覽等功能,需要的可以參考下

最近項(xiàng)目中需要處理大量文件存儲(chǔ)和管理的需求,對(duì)比了 Nginx、FastDFS、阿里云 OSS 等多種方案后,最終選擇了 MinIO。今天就來(lái)和大家分享一下 SpringBoot 整合 MinIO 的全過(guò)程,從為什么選擇 MinIO,到各種文件操作的實(shí)現(xiàn),包括簡(jiǎn)單的文件上傳、批量上傳、文件下載、文件預(yù)覽,再到大文件分片上傳和秒傳功能。

一、為什么選擇 MinIO

在選擇文件存儲(chǔ)方案時(shí),我們需要考慮多個(gè)因素,如功能、性能、成本、擴(kuò)展性等。對(duì)比其他常見(jiàn)的文件存儲(chǔ)方案,MinIO 具有以下優(yōu)勢(shì):

1. 功能豐富

MinIO 支持標(biāo)準(zhǔn)的 S3 協(xié)議,可以與其他支持 S3 協(xié)議的工具和服務(wù)無(wú)縫集成。同時(shí),它還提供了豐富的 API,包括文件上傳、下載、預(yù)覽、刪除、版本控制等,滿足各種文件管理需求。

2. 高性能

MinIO 專(zhuān)為高性能設(shè)計(jì),采用分布式架構(gòu),可以橫向擴(kuò)展,支持 PB 級(jí)數(shù)據(jù)存儲(chǔ)。在讀寫(xiě)性能方面,MinIO 表現(xiàn)出色,尤其適合大文件的存儲(chǔ)和處理。

3. 開(kāi)源免費(fèi)

MinIO 是開(kāi)源項(xiàng)目,采用 AGPL v3 許可證,企業(yè)可以免費(fèi)使用。對(duì)于中小企業(yè)來(lái)說(shuō),這無(wú)疑是一個(gè)很大的優(yōu)勢(shì)。

4. 易于部署和管理

MinIO 提供了簡(jiǎn)單易用的命令行工具和 Web 界面,部署和管理都非常方便??梢栽趲追昼妰?nèi)完成部署,并開(kāi)始使用。

5. 數(shù)據(jù)安全

MinIO 支持?jǐn)?shù)據(jù)加密、訪問(wèn)控制、多因素認(rèn)證等安全功能,保障數(shù)據(jù)的安全性和隱私性。

對(duì)比其他方案

  • Nginx:主要用于靜態(tài)文件服務(wù),不支持分布式存儲(chǔ)和大規(guī)模文件管理。
  • FastDFS:功能相對(duì)簡(jiǎn)單,缺乏統(tǒng)一的管理界面,擴(kuò)展性有限。
  • 阿里云 OSS:云服務(wù)成本較高,依賴(lài)于網(wǎng)絡(luò)環(huán)境,不適合對(duì)數(shù)據(jù)隱私要求較高的場(chǎng)景。

綜上所述,MinIO 是一個(gè)功能強(qiáng)大、性能出色、易于部署和管理的文件存儲(chǔ)方案,非常適合作為企業(yè)級(jí)文件存儲(chǔ)系統(tǒng)。

二、環(huán)境準(zhǔn)備

1. 安裝 MinIO

可以通過(guò) Docker 快速安裝 MinIO:

docker run -p 9000:9000 -p 9001:9001 \
  --name minio \
  -v /data/minio/data:/data \
  -v /data/minio/config:/root/.minio \
  -e "MINIO_ROOT_USER=minioadmin" \
  -e "MINIO_ROOT_PASSWORD=minioadmin" \
  minio/minio server /data --console-address ":9001"

安裝完成后,可以通過(guò)訪問(wèn)http://localhost:9001進(jìn)入 MinIO 管理界面,使用用戶名minioadmin和密碼minioadmin登錄。

2. 創(chuàng)建 SpringBoot 項(xiàng)目

使用 Spring Initializr 創(chuàng)建一個(gè) SpringBoot 項(xiàng)目,添加以下依賴(lài):

  • Spring Web
  • Lombok
  • MinIO Client

三、整合 MinIO

1. 添加依賴(lài)

pom.xml中添加 MinIO 客戶端依賴(lài):

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

2. 配置 MinIO 連接信息

application.yml中添加 MinIO 配置信息:

minio:
  endpoint: http://localhost:9000
  access-key: minioadmin
  secret-key: minioadmin
  bucket-name: test-bucket

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

創(chuàng)建一個(gè)配置類(lèi),用于創(chuàng)建 MinIO 客戶端:

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

四、創(chuàng)建 MinIO 工具類(lèi)

為了方便使用 MinIO 的各種功能,我們創(chuàng)建一個(gè)工具類(lèi),封裝 MinIO 的常用操作:

import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class MinioUtil {

    @Autowired
    private MinioClient minioClient;

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

    /**
     * 檢查存儲(chǔ)桶是否存在
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @return 是否存在
     */
    @SneakyThrows
    public boolean bucketExists(String bucketName) {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 創(chuàng)建存儲(chǔ)桶
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     */
    @SneakyThrows
    public void makeBucket(String bucketName) {
        if (!bucketExists(bucketName)) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 獲取所有存儲(chǔ)桶
     * @return 存儲(chǔ)桶列表
     */
    @SneakyThrows
    public List<Bucket> listBuckets() {
        return minioClient.listBuckets();
    }

    /**
     * 刪除存儲(chǔ)桶
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     */
    @SneakyThrows
    public void removeBucket(String bucketName) {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 簡(jiǎn)單文件上傳
     * @param file       文件
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @return 文件信息
     */
    @SneakyThrows
    public Map<String, String> uploadFile(MultipartFile file, String bucketName) {
        if (file == null || file.isEmpty()) {
            return null;
        }

        if (!bucketExists(bucketName)) {
            makeBucket(bucketName);
        }

        String originalFilename = file.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(fileName)
                .contentType(file.getContentType())
                .stream(file.getInputStream(), file.getSize(), -1)
                .build());

        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("fileName", fileName);
        resultMap.put("originalFilename", originalFilename);
        resultMap.put("url", getObjectUrl(bucketName, fileName, 7));

        return resultMap;
    }

    /**
     * 簡(jiǎn)單文件上傳(使用默認(rèn)存儲(chǔ)桶)
     * @param file 文件
     * @return 文件信息
     */
    public Map<String, String> uploadFile(MultipartFile file) {
        return uploadFile(file, defaultBucketName);
    }

    /**
     * 批量文件上傳
     * @param files      文件列表
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @return 文件信息列表
     */
    public List<Map<String, String>> uploadFiles(List<MultipartFile> files, String bucketName) {
        return files.stream()
                .map(file -> uploadFile(file, bucketName))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 批量文件上傳(使用默認(rèn)存儲(chǔ)桶)
     * @param files 文件列表
     * @return 文件信息列表
     */
    public List<Map<String, String>> uploadFiles(List<MultipartFile> files) {
        return uploadFiles(files, defaultBucketName);
    }

    /**
     * 下載文件
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @param objectName 對(duì)象名稱(chēng)
     * @return 輸入流
     */
    @SneakyThrows
    public InputStream downloadFile(String bucketName, String objectName) {
        return minioClient.getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    /**
     * 下載文件(使用默認(rèn)存儲(chǔ)桶)
     * @param objectName 對(duì)象名稱(chēng)
     * @return 輸入流
     */
    public InputStream downloadFile(String objectName) {
        return downloadFile(defaultBucketName, objectName);
    }

    /**
     * 刪除文件
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @param objectName 對(duì)象名稱(chēng)
     */
    @SneakyThrows
    public void deleteFile(String bucketName, String objectName) {
        minioClient.removeObject(RemoveObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    /**
     * 刪除文件(使用默認(rèn)存儲(chǔ)桶)
     * @param objectName 對(duì)象名稱(chēng)
     */
    public void deleteFile(String objectName) {
        deleteFile(defaultBucketName, objectName);
    }

    /**
     * 批量刪除文件
     * @param bucketName  存儲(chǔ)桶名稱(chēng)
     * @param objectNames 對(duì)象名稱(chēng)列表
     * @return 刪除錯(cuò)誤列表
     */
    @SneakyThrows
    public List<DeleteError> deleteFiles(String bucketName, List<String> objectNames) {
        List<DeleteObject> objects = objectNames.stream()
                .map(DeleteObject::new)
                .collect(Collectors.toList());

        Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder()
                .bucket(bucketName)
                .objects(objects)
                .build());

        List<DeleteError> errors = new ArrayList<>();
        for (Result<DeleteError> result : results) {
            errors.add(result.get());
        }
        return errors;
    }

    /**
     * 批量刪除文件(使用默認(rèn)存儲(chǔ)桶)
     * @param objectNames 對(duì)象名稱(chēng)列表
     * @return 刪除錯(cuò)誤列表
     */
    public List<DeleteError> deleteFiles(List<String> objectNames) {
        return deleteFiles(defaultBucketName, objectNames);
    }

    /**
     * 獲取文件URL
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @param objectName 對(duì)象名稱(chēng)
     * @param expires    過(guò)期時(shí)間(天)
     * @return 文件URL
     */
    @SneakyThrows
    public String getObjectUrl(String bucketName, String objectName, int expires) {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(bucketName)
                .object(objectName)
                .expiry(expires, TimeUnit.DAYS)
                .build());
    }

    /**
     * 獲取文件URL(使用默認(rèn)存儲(chǔ)桶)
     * @param objectName 對(duì)象名稱(chēng)
     * @param expires    過(guò)期時(shí)間(天)
     * @return 文件URL
     */
    public String getObjectUrl(String objectName, int expires) {
        return getObjectUrl(defaultBucketName, objectName, expires);
    }

    /**
     * 檢查文件是否存在
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @param objectName 對(duì)象名稱(chēng)
     * @return 是否存在
     */
    @SneakyThrows
    public boolean objectExists(String bucketName, String objectName) {
        try {
            minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 檢查文件是否存在(使用默認(rèn)存儲(chǔ)桶)
     * @param objectName 對(duì)象名稱(chēng)
     * @return 是否存在
     */
    public boolean objectExists(String objectName) {
        return objectExists(defaultBucketName, objectName);
    }

    /**
     * 列出存儲(chǔ)桶中的所有對(duì)象
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @return 對(duì)象列表
     */
    @SneakyThrows
    public List<Item> listObjects(String bucketName) {
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(bucketName)
                .build());

        List<Item> items = new ArrayList<>();
        for (Result<Item> result : results) {
            items.add(result.get());
        }
        return items;
    }

    /**
     * 列出存儲(chǔ)桶中的所有對(duì)象(使用默認(rèn)存儲(chǔ)桶)
     * @return 對(duì)象列表
     */
    public List<Item> listObjects() {
        return listObjects(defaultBucketName);
    }

    /**
     * 創(chuàng)建分片上傳
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @param objectName 對(duì)象名稱(chēng)
     * @return 上傳ID
     */
    @SneakyThrows
    public String createMultipartUpload(String bucketName, String objectName) {
        CreateMultipartUploadResponse response = minioClient.createMultipartUpload(CreateMultipartUploadArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
        return response.result().uploadId();
    }

    /**
     * 上傳分片
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @param objectName 對(duì)象名稱(chēng)
     * @param uploadId   上傳ID
     * @param partNumber 分片編號(hào)
     * @param stream     輸入流
     * @param size       大小
     * @return 分片ETag
     */
    @SneakyThrows
    public String uploadPart(String bucketName, String objectName, String uploadId, int partNumber, InputStream stream, long size) {
        UploadPartResponse response = minioClient.uploadPart(UploadPartArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .uploadId(uploadId)
                .partNumber(partNumber)
                .stream(stream, size, -1)
                .build());
        return response.etag();
    }

    /**
     * 完成分片上傳
     * @param bucketName 存儲(chǔ)桶名稱(chēng)
     * @param objectName 對(duì)象名稱(chēng)
     * @param uploadId   上傳ID
     * @param etags      分片ETag列表
     */
    @SneakyThrows
    public void completeMultipartUpload(String bucketName, String objectName, String uploadId, List<String> etags) {
        List<CompletePart> completeParts = new ArrayList<>();
        for (int i = 0; i < etags.size(); i++) {
            completeParts.add(new CompletePart(i + 1, etags.get(i)));
        }

        minioClient.completeMultipartUpload(CompleteMultipartUploadArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .uploadId(uploadId)
                .parts(completeParts)
                .build());
    }

    /**
     * 生成文件哈希值(用于秒傳判斷)
     * @param file 文件
     * @return 哈希值
     */
    @SneakyThrows
    public String generateFileHash(MultipartFile file) {
        // 這里使用簡(jiǎn)單的文件大小和修改時(shí)間作為哈希值,實(shí)際應(yīng)用中應(yīng)使用MD5或SHA-1等算法
        return file.getSize() + "-" + file.getOriginalFilename();
    }
}

五、創(chuàng)建 Controller

接下來(lái),我們創(chuàng)建一個(gè) Controller,提供各種文件操作的接口:

import io.minio.messages.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
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.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

    @Autowired
    private MinioUtil minioUtil;

    /**
     * 簡(jiǎn)單文件上傳
     */
    @PostMapping("/upload")
    public ResponseEntity<Map<String, Object>> uploadFile(@RequestParam("file") MultipartFile file) {
        Map<String, Object> result = new HashMap<>();
        try {
            Map<String, String> fileInfo = minioUtil.uploadFile(file);
            if (fileInfo != null) {
                result.put("code", 200);
                result.put("message", "上傳成功");
                result.put("data", fileInfo);
                return ResponseEntity.ok(result);
            } else {
                result.put("code", 500);
                result.put("message", "上傳失敗");
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
            }
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "上傳異常:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }

    /**
     * 批量文件上傳
     */
    @PostMapping("/upload/batch")
    public ResponseEntity<Map<String, Object>> uploadFiles(@RequestParam("files") List<MultipartFile> files) {
        Map<String, Object> result = new HashMap<>();
        try {
            List<Map<String, String>> fileInfos = minioUtil.uploadFiles(files);
            result.put("code", 200);
            result.put("message", "上傳成功");
            result.put("data", fileInfos);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "上傳異常:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }

    /**
     * 文件下載
     */
    @GetMapping("/download/{fileName}")
    public ResponseEntity<byte[]> downloadFile(@PathVariable("fileName") String fileName) {
        try {
            InputStream inputStream = minioUtil.downloadFile(fileName);
            byte[] bytes = inputStream.readAllBytes();
            inputStream.close();

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

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

    /**
     * 文件預(yù)覽
     */
    @GetMapping("/preview/{fileName}")
    public ResponseEntity<Map<String, Object>> previewFile(@PathVariable("fileName") String fileName) {
        Map<String, Object> result = new HashMap<>();
        try {
            String url = minioUtil.getObjectUrl(fileName, 1);
            result.put("code", 200);
            result.put("message", "獲取成功");
            result.put("url", url);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "獲取異常:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }

    /**
     * 刪除文件
     */
    @DeleteMapping("/delete/{fileName}")
    public ResponseEntity<Map<String, Object>> deleteFile(@PathVariable("fileName") String fileName) {
        Map<String, Object> result = new HashMap<>();
        try {
            minioUtil.deleteFile(fileName);
            result.put("code", 200);
            result.put("message", "刪除成功");
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "刪除異常:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }

    /**
     * 列出所有文件
     */
    @GetMapping("/list")
    public ResponseEntity<Map<String, Object>> listFiles() {
        Map<String, Object> result = new HashMap<>();
        try {
            List<Item> items = minioUtil.listObjects();
            result.put("code", 200);
            result.put("message", "獲取成功");
            result.put("data", items);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "獲取異常:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }

    /**
     * 初始化分片上傳
     */
    @PostMapping("/multipart/init")
    public ResponseEntity<Map<String, Object>> initMultipartUpload(@RequestParam("fileName") String fileName) {
        Map<String, Object> result = new HashMap<>();
        try {
            String uploadId = minioUtil.createMultipartUpload("test-bucket", fileName);
            result.put("code", 200);
            result.put("message", "初始化成功");
            result.put("uploadId", uploadId);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "初始化異常:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }

    /**
     * 上傳分片
     */
    @PostMapping("/multipart/upload")
    public ResponseEntity<Map<String, Object>> uploadPart(
            @RequestParam("fileName") String fileName,
            @RequestParam("uploadId") String uploadId,
            @RequestParam("partNumber") int partNumber,
            @RequestParam("file") MultipartFile file) {
        Map<String, Object> result = new HashMap<>();
        try {
            String etag = minioUtil.uploadPart("test-bucket", fileName, uploadId, partNumber, file.getInputStream(), file.getSize());
            result.put("code", 200);
            result.put("message", "分片上傳成功");
            result.put("etag", etag);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "分片上傳異常:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }

    /**
     * 完成分片上傳
     */
    @PostMapping("/multipart/complete")
    public ResponseEntity<Map<String, Object>> completeMultipartUpload(
            @RequestParam("fileName") String fileName,
            @RequestParam("uploadId") String uploadId,
            @RequestParam("etags") List<String> etags) {
        Map<String, Object> result = new HashMap<>();
        try {
            minioUtil.completeMultipartUpload("test-bucket", fileName, uploadId, etags);
            result.put("code", 200);
            result.put("message", "分片合并成功");
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "分片合并異常:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }

    /**
     * 文件秒傳檢查
     */
    @PostMapping("/check")
    public ResponseEntity<Map<String, Object>> checkFile(@RequestParam("file") MultipartFile file) {
        Map<String, Object> result = new HashMap<>();
        try {
            String fileHash = minioUtil.generateFileHash(file);
            // 這里應(yīng)該查詢(xún)數(shù)據(jù)庫(kù)或緩存,檢查是否存在相同哈希值的文件
            // 為簡(jiǎn)化示例,直接返回不存在
            boolean exists = false;

            result.put("code", 200);
            result.put("message", "檢查成功");
            result.put("exists", exists);
            result.put("fileHash", fileHash);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "檢查異常:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }
}

六、大文件分片上傳和秒傳實(shí)現(xiàn)原理

1. 大文件分片上傳

大文件分片上傳是將一個(gè)大文件分成多個(gè)小片段,分別上傳這些片段,最后在服務(wù)器端將這些片段合并成一個(gè)完整的文件。實(shí)現(xiàn)步驟如下:

前端:將文件切成固定大小的片段(如 1MB / 片),為每個(gè)片段生成唯一標(biāo)識(shí)(如序號(hào)),按順序上傳這些片段。

后端

  • 接收前端上傳的片段,保存到臨時(shí)目錄。
  • 記錄已上傳的片段信息(如文件名、片段序號(hào)、ETag 等)。
  • 當(dāng)所有片段上傳完成后,按順序合并這些片段。

2. 秒傳功能

秒傳功能是指當(dāng)用戶上傳一個(gè)文件時(shí),系統(tǒng)首先檢查該文件是否已經(jīng)存在,如果存在則直接返回文件鏈接,無(wú)需重新上傳。實(shí)現(xiàn)步驟如下:

前端:計(jì)算文件的哈希值(如 MD5、SHA-1),并將哈希值發(fā)送給后端。

后端

  • 根據(jù)哈希值查詢(xún)數(shù)據(jù)庫(kù)或緩存,檢查是否存在相同哈希值的文件。
  • 如果存在,返回文件鏈接;如果不存在,通知前端正常上傳。

七、測(cè)試與驗(yàn)證

1. 簡(jiǎn)單文件上傳測(cè)試

使用 Postman 或其他工具,向/api/minio/upload接口發(fā)送 POST 請(qǐng)求,上傳一個(gè)文件,驗(yàn)證是否能成功上傳并返回文件信息。

2. 批量文件上傳測(cè)試

/api/minio/upload/batch接口發(fā)送 POST 請(qǐng)求,上傳多個(gè)文件,驗(yàn)證是否能成功批量上傳。

3. 文件下載測(cè)試

訪問(wèn)/api/minio/download/{fileName}接口,驗(yàn)證是否能成功下載文件。

4. 文件預(yù)覽測(cè)試

訪問(wèn)/api/minio/preview/{fileName}接口,驗(yàn)證是否能獲取文件預(yù)覽鏈接。

5. 大文件分片上傳測(cè)試

使用前端工具(如 webuploader、plupload 等)實(shí)現(xiàn)大文件分片上傳功能,調(diào)用后端提供的分片上傳接口,驗(yàn)證大文件是否能成功上傳。

6. 秒傳功能測(cè)試

上傳一個(gè)文件,記錄文件哈希值,再次上傳相同文件,驗(yàn)證是否能秒傳成功。

到此這篇關(guān)于SpringBoot整合MinIO實(shí)現(xiàn)全場(chǎng)景文件操作管理的文章就介紹到這了,更多相關(guān)SpringBoot MinIO文件操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

铁岭县| 土默特左旗| 社会| 巴东县| 绵阳市| 乾安县| 赤峰市| 云霄县| 章丘市| 酉阳| 来安县| 邵武市| 高青县| 汉阴县| 天长市| 平顶山市| 仁寿县| 丰都县| 黄龙县| 玉屏| 湟源县| 田阳县| 原阳县| 富阳市| 潜江市| 介休市| 建水县| 尼勒克县| 东明县| 栖霞市| 和龙市| 叶城县| 延安市| 福贡县| 旺苍县| 遂昌县| 临高县| 三明市| 晋中市| 荔浦县| 隆化县|