MinIO集成Spring完成分片上传、秒传、断点续传-version-8.5.3

首先在pom文件中添加依赖:

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

在MinIO-8.5.3版本中 MinioClient中调用方法已经从本身调用转换成了其中的私有属性MinioAsyncClient完成异步调用。因此继承本身的MinioClient无法调用S3Base中的方法,

需要继承MinioAsyncClient去调用。

public class PearlMinioClient extends MinioAsyncClient {


    protected PearlMinioClient(MinioAsyncClient client) {
        super(client);
    }

    /**
     * 创建分片上传请求
     *
     * @param bucketName       存储桶
     * @param region           区域
     * @param objectName       对象名
     * @param headers          消息头
     * @param extraQueryParams 额外查询参数
     */
    @Override
    public CompletableFuture<CreateMultipartUploadResponse> createMultipartUploadAsync(String bucketName, String region, String objectName, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws  InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException {
        return super.createMultipartUploadAsync(bucketName, region, objectName, headers, extraQueryParams);
    }

    /**
     * 完成分片上传,执行合并文件
     *
     * @param bucketName       存储桶
     * @param region           区域
     * @param objectName       对象名
     * @param uploadId         上传ID
     * @param parts            分片
     * @param extraHeaders     额外消息头
     * @param extraQueryParams 额外查询参数
     */
    @Override
    public CompletableFuture<ObjectWriteResponse> completeMultipartUploadAsync(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, XmlParserException, InternalException {
        return super.completeMultipartUploadAsync(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams);
    }

    /**
     * 查询分片数据
     *
     * @param bucketName       存储桶
     * @param region           区域
     * @param objectName       对象名
     * @param uploadId         上传ID
     * @param extraHeaders     额外消息头
     * @param extraQueryParams 额外查询参数
     */
    public CompletableFuture<ListPartsResponse> listMultipart(String bucketName, String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {
        return super.listPartsAsync(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);
    }
}

有这三个方法之后就比较简单了。

首先确定逻辑 应该是有一个文件标识上传之后,判断之前是否有上传的过程 有的话返回查询过的数据,没有的话就初始化一个分片任务 ,完成上传之后进行合并即可。

Service层,判断之前是否有上传

    public TaskInfoDTO getPartInfo(String identifier) throws Exception {
        MultipartFileInfo multipartFileInfo = multipartFileDao.selectByIdentifier(identifier);
        if (multipartFileInfo==null){
            return null;
        }

        String bucketName = multipartFileInfo.getBucketName();
        String objectName = multipartFileInfo.getFilePath();
        TaskInfoDTO taskInfo = new TaskInfoDTO(true, getPath(bucketName, objectName), MultipartFileDTO.convertFromEntity(multipartFileInfo));
        if (multipartFileInfo.getIsComplete()==1){
            return taskInfo;
        }else {
            ListPartsResponse result = pearlMinioClient.listMultipart(bucketName, null, objectName, 10000, 0, multipartFileInfo.getUploadId(), null, null).get();
            List<Part> partList = result.result().partList();
            taskInfo.getMultipartFileDTO().setExitPartList(MultipartFileDTO.exchangeExitPartList(partList));
            taskInfo.setFinished(false);
        }
        return taskInfo;

    }

获取上传的地址 给前端去直接将文件上传到minIO

    @Override
    public String preUrl(String identifier,int partNumber) throws Exception  {
        MultipartFileInfo multipartFileInfo = multipartFileDao.selectByIdentifier(identifier);
        if (multipartFileInfo == null) {
            throw new RuntimeException("分片任务不存在");
        }
        Map<String, String> params = new HashMap<>();
        params.put("partNumber", String.valueOf(partNumber));
        params.put("uploadId", multipartFileInfo.getUploadId());
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .bucket(multipartFileInfo.getBucketName()).object(multipartFileInfo.getFilePath())
                .method(Method.PUT).expiry(expiry).extraQueryParams(params).build());
    }

如果之前的判断之前是否上传的记录返回为null 那么就去初始化一个任务

    @Override
    public TaskInfoDTO createMultipart(String fileIdentifier,String bucketName, String fileName, long chunkSize,long totalSize) throws Exception {
        //判断有无该桶
        bucketName = getBuckets(bucketName) ? bucketName : "";
        if(Strings.isBlank(bucketName)){
            bucketName = MinioProperties.MINIO_BUCKET_NAME;
        }else if(!getBuckets(bucketName)){
            throw new RuntimeException("不存在该桶");
        }
        String suffix = fileName.substring(fileName.lastIndexOf(".")+1);
        String key = com.boppod.iplatform.common.core.utils.StringUtils.format("{}/{}.{}", DateUtils.datePath(), IdUtils.randomUUID(), suffix);
        CreateMultipartUploadResponse response = pearlMinioClient.createMultipartUploadAsync(bucketName, null, key, null, null).get();
        String uploadId = response.result().uploadId();
        Map<String, String> reqParams = new HashMap<>();
        reqParams.put("uploadId", uploadId);
        for (int i = 1; i <= chunkSize; i++) {
            reqParams.put("partNumber", String.valueOf(i));
        }
        int chunkNum = (int) Math.ceil(totalSize * 1.0 / chunkSize);
        //将信息存入数据库中

       return new TaskInfoDTO(false, getPath(bucketName, key), MultipartFileDTO.convertFromEntity(multipartfileInfo));



    }

上传完成之后合并分片任务即可

  @Override
    public boolean completeMultipart(String identifier)  {
        try {
            Part[] parts = new Part[1000];
            MultipartFileInfo multipartFileInfo = multipartFileDao.selectByIdentifier(identifier);
            if (multipartFileInfo==null){
                throw new RuntimeException("分片任务不存在");
            }
            String objectName = multipartFileInfo.getFilePath();
            String bucketName = multipartFileInfo.getBucketName();
            String uploadId = multipartFileInfo.getUploadId();
            CompletableFuture<ListPartsResponse> future = pearlMinioClient.listMultipart(bucketName, null, objectName, 1000, 0, uploadId, null, null);
            ListPartsResponse response = future.get();
            List<Part> partList = response.result().partList();
            if (partList.size()!= multipartFileInfo.getChunkNum()){
                // 已上传分块数量与记录中的数量不对应,不能合并分块
                throw new RuntimeException("分片缺失,请重新上传");
            }
            int partNum=1;
            for (int i = 0; i < partList.size(); i++) {
                parts[partNum-1]=new Part(partNum,partList.get(i).etag());
                partNum++;
            }
            pearlMinioClient.completeMultipartUploadAsync(bucketName, null, objectName, uploadId, parts, null, null);
            multipartFileDao.uploadByIdetifier(identifier);
        }catch (Exception e){
            return false;
        }
        return true;
    }

参考文章:

        Gitee上的minio-upload-api ;

        csdn上云烟成雨TD的MinIO教学。

minio-upload-api有现成的前端代码可以测试。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
非常感谢您的问题。关于您的问题,我会尽我所能地为您提供帮助。首先,Spring Boot是一种快速开发的Web框架,而MinIO是一种高性能、轻量级的对象存储服务。集成MinIO可以帮助您将文件存储到云存储中,以便在不同应用之间共享。分片上传断点续传和进度条是MinIO的一些高级功能,可以帮助您在上传大文件时更加灵活和高效。 关于如何集成MinIO和实现分片上传断点续传、进度条,通常可以通过以下步骤完成: 1. 首先,需要在Spring Boot中添加MinIO客户端的依赖,例如: ```xml <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.1.7</version> </dependency> ``` 2. 然后,需要配置MinIO客户端的连接信息,例如: ```java @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(); } } ``` 3. 接下来,可以封装一些工具类来实现分片上传断点续传和进度条等功能,例如: ```java @Service public class MinioService { private static final String BUCKET_NAME = "test"; @Autowired private MinioClient minioClient; /** * 上传文件 * @param file 文件 * @param objectName 对象名称 */ public void uploadObject(File file, String objectName) { try { PutObjectArgs args = PutObjectArgs.builder() .bucket(BUCKET_NAME) .object(objectName) .stream(new FileInputStream(file), file.length(), -1) .build(); minioClient.putObject(args); } catch (Exception e) { throw new RuntimeException("上传文件失败", e); } } /** * 断点续传 * @param file 文件 * @param objectName 对象名称 */ public void resumeUploadObject(File file, String objectName) { try { UploadObjectArgs args = UploadObjectArgs.builder() .bucket(BUCKET_NAME) .object(objectName) .stream(new FileInputStream(file), file.length(), -1) .build(); minioClient.uploadObject(args); } catch (Exception e) { throw new RuntimeException("上传文件失败", e); } } /** * 分片上传 * @param file 文件 * @param objectName 对象名称 * @param partSize 分片大小 */ public void multipartUploadObject(File file, String objectName, long partSize) { try { InitiateMultipartUploadArgs initArgs = InitiateMultipartUploadArgs.builder() .bucket(BUCKET_NAME) .object(objectName) .build(); String uploadId = minioClient.initiateMultipartUpload(initArgs); int partCount = (int) (file.length() / partSize) + 1; List<String> partEtags = new ArrayList<>(partCount); for (int i = 0; i < partCount; i++) { long offset = i * partSize; long size = Math.min(file.length() - offset, partSize); UploadPartArgs uploadArgs = UploadPartArgs.builder() .bucket(BUCKET_NAME) .object(objectName) .uploadId(uploadId) .partNumber(i + 1) .stream(new FileInputStream(file), size, offset) .build(); String etag = minioClient.uploadPart(uploadArgs); partEtags.add(etag); } CompleteMultipartUploadArgs completeArgs = CompleteMultipartUploadArgs.builder() .bucket(BUCKET_NAME) .object(objectName) .uploadId(uploadId) .parts(partEtags) .build(); minioClient.completeMultipartUpload(completeArgs); } catch (Exception e) { throw new RuntimeException("上传文件失败", e); } } } ``` 4. 最后,在Controller中可以调用MinioService的方法来实现文件上传,例如: ```java @RestController public class UploadController { @Autowired private MinioService minioService; @PostMapping("/upload") public String upload(@RequestParam("file") MultipartFile file) throws Exception { String objectName = file.getOriginalFilename(); File tempFile = File.createTempFile("temp-", "-" + objectName); file.transferTo(tempFile); minioService.uploadObject(tempFile, objectName); //minioService.resumeUploadObject(tempFile, objectName); //minioService.multipartUploadObject(tempFile, objectName, 5 * 1024 * 1024); return "success"; } } ``` 希望以上内容能够帮助到您,如果您有任何其他问题或疑问,请随时与我交流。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值