欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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ù)成本較高,依賴于網(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)目,添加以下依賴:

  • Spring Web
  • Lombok
  • MinIO Client

三、整合 MinIO

1. 添加依賴

pom.xml中添加 MinIO 客戶端依賴:

<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ǔ)桶名稱
     * @return 是否存在
     */
    @SneakyThrows
    public boolean bucketExists(String bucketName) {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 創(chuàng)建存儲(chǔ)桶
     * @param bucketName 存儲(chǔ)桶名稱
     */
    @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ǔ)桶名稱
     */
    @SneakyThrows
    public void removeBucket(String bucketName) {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 簡(jiǎn)單文件上傳
     * @param file       文件
     * @param bucketName 存儲(chǔ)桶名稱
     * @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ǔ)桶名稱
     * @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ǔ)桶名稱
     * @param objectName 對(duì)象名稱
     * @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ì)象名稱
     * @return 輸入流
     */
    public InputStream downloadFile(String objectName) {
        return downloadFile(defaultBucketName, objectName);
    }

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

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

    /**
     * 批量刪除文件
     * @param bucketName  存儲(chǔ)桶名稱
     * @param objectNames 對(duì)象名稱列表
     * @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ì)象名稱列表
     * @return 刪除錯(cuò)誤列表
     */
    public List<DeleteError> deleteFiles(List<String> objectNames) {
        return deleteFiles(defaultBucketName, objectNames);
    }

    /**
     * 獲取文件URL
     * @param bucketName 存儲(chǔ)桶名稱
     * @param objectName 對(duì)象名稱
     * @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ì)象名稱
     * @param expires    過(guò)期時(shí)間(天)
     * @return 文件URL
     */
    public String getObjectUrl(String objectName, int expires) {
        return getObjectUrl(defaultBucketName, objectName, expires);
    }

    /**
     * 檢查文件是否存在
     * @param bucketName 存儲(chǔ)桶名稱
     * @param objectName 對(duì)象名稱
     * @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ì)象名稱
     * @return 是否存在
     */
    public boolean objectExists(String objectName) {
        return objectExists(defaultBucketName, objectName);
    }

    /**
     * 列出存儲(chǔ)桶中的所有對(duì)象
     * @param bucketName 存儲(chǔ)桶名稱
     * @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ǔ)桶名稱
     * @param objectName 對(duì)象名稱
     * @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ǔ)桶名稱
     * @param objectName 對(duì)象名稱
     * @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ǔ)桶名稱
     * @param objectName 對(duì)象名稱
     * @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)該查詢數(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ù)哈希值查詢數(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)文章

  • 如何利用MyBatisX插件自動(dòng)生成代碼

    如何利用MyBatisX插件自動(dòng)生成代碼

    這篇文章主要介紹了如何利用MyBatisX插件自動(dòng)生成代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • java 多線程實(shí)現(xiàn)在線咨詢(udp)

    java 多線程實(shí)現(xiàn)在線咨詢(udp)

    這篇文章主要介紹了java 多線程實(shí)現(xiàn)在線咨詢(udp)的示例,幫助大家更好的理解和學(xué)習(xí)Java 網(wǎng)絡(luò)編程的相關(guān)內(nèi)容,感興趣的朋友可以了解下
    2020-11-11
  • Java重寫(xiě)(Override)與重載(Overload)區(qū)別原理解析

    Java重寫(xiě)(Override)與重載(Overload)區(qū)別原理解析

    這篇文章主要介紹了Java重寫(xiě)(Override)與重載(Overload)區(qū)別原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • springboot切面添加日志功能實(shí)例詳解

    springboot切面添加日志功能實(shí)例詳解

    在本篇文章里小編給大家整理的是關(guān)于springboot 切面添加日志功能的相關(guān)知識(shí)點(diǎn)內(nèi)容,有需要的朋友們可以參考下。
    2019-09-09
  • Java 中函數(shù) Function 的使用和定義示例小結(jié)

    Java 中函數(shù) Function 的使用和定義示例小結(jié)

    這篇文章主要介紹了Java 中函數(shù) Function 的使用和定義小結(jié),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-07-07
  • 淺談MyBatis Plus主鍵設(shè)置策略

    淺談MyBatis Plus主鍵設(shè)置策略

    本文主要介紹了MyBatis Plus主鍵設(shè)置策略,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Java中二叉樹(shù)的先序、中序、后序遍歷以及代碼實(shí)現(xiàn)

    Java中二叉樹(shù)的先序、中序、后序遍歷以及代碼實(shí)現(xiàn)

    這篇文章主要介紹了Java中二叉樹(shù)的先序、中序、后序遍歷以及代碼實(shí)現(xiàn),一棵二叉樹(shù)是結(jié)點(diǎn)的一個(gè)有限集合,該集合或者為空,或者是由一個(gè)根節(jié)點(diǎn)加上兩棵別稱為左子樹(shù)和右子樹(shù)的二叉樹(shù)組成,需要的朋友可以參考下
    2023-11-11
  • springboot3+r2dbc響應(yīng)式編程實(shí)踐

    springboot3+r2dbc響應(yīng)式編程實(shí)踐

    本文主要介紹了springboot3+r2dbc響應(yīng)式編程實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • SpringBoot項(xiàng)目中如何解決跨域問(wèn)題的最新方案?

    SpringBoot項(xiàng)目中如何解決跨域問(wèn)題的最新方案?

    跨域問(wèn)題是瀏覽器為了保護(hù)用戶的信息安全,實(shí)施了同源策略(Same-Origin Policy),即只允許頁(yè)面請(qǐng)求同源(相同協(xié)議、域名和端口)的資源,當(dāng) JavaScript 發(fā)起的請(qǐng)求跨越了同源策略,即請(qǐng)求的目標(biāo)與當(dāng)前頁(yè)面的域名、端口、協(xié)議不一致時(shí),瀏覽器會(huì)阻止請(qǐng)求的發(fā)送或接收
    2025-03-03
  • 通過(guò)實(shí)例學(xué)習(xí)Spring @Required注釋原理

    通過(guò)實(shí)例學(xué)習(xí)Spring @Required注釋原理

    這篇文章主要介紹了通過(guò)實(shí)例學(xué)習(xí)Spring @Required注釋原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03

最新評(píng)論