springBoot集成minio,实现分片、断点续传

前言

近期的项目中涉及到1G以上的视频上传,除了考虑上传效率之外,还要考虑到用户在上传过程中出现断网,浏览器意外关闭等情况。下面是我调研的两种处理方式:

一、传统的处理方案是在本地新建临时文件夹,储存视频分片,等全部上传完成后进行合并,然后删除临时文件夹,这样做会存在问题:比如多个用户都上传了一半视频不想上传了,没有进行合并,那么将会大致大量的临时文件夹保存在服务器,造成空间浪费。这里还需要自己想办法手动清理临时文件中保存的视频分片。

二、通过Minio实现分片及断点续传,该方案可以解决上述传统方案中所产生的问题,minio自己会去处理没有上传完的分片。下面记录一下我的使用过程。

Minio集成

思路:

1、前端根据视频文件计算MD5,MD5用来做断点续传,如果不需要断点续传可以不计算MD5。

2、前端掉用后端接口获取切片临时上传地址,获取地址后,前端直连Minio将切片视频进行上传

3、如果出现上传中断,再次选择该文件上传时,后端根据MD5去数据库查取数据,并调用minio的listParts方法获取已上传的切片,后端处理数据后,响应给前端未上传的切片上传地址,前端拿到后将未上传的切分进行上传。

4、前端在所有切片上传完成后,调用后端的completeMultipartUpload方法合并所有切片。

代码:

依赖引入

<dependency>
     <groupId>io.minio</groupId>
     <artifactId>minio</artifactId>
     <version>${minio.version}</version>
</dependency>

minio连接配置类:  MinioConfig

public class MinioConfig {

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


    /**
     * 注入minio 客户端
     * @return
     */
    @Bean
    public MinioUtils minioClient(){
 
        MinioClient minioClient = MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
        return new MinioUtils(minioClient);
    }

}

minio工具类:MinioClient

public class MinioUtils extends MinioClient {
   
    public MinioUtilS(MinioClient client) {
        super(client);
    }

    /**
     * 上传分片上传请求,返回uploadId
     * @param bucketName       存储桶
     * @param region           区域
     * @param objectName       对象名
     * @param headers           消息头
     * @param extraQueryParams 额外查询参数
     */
    public CreateMultipartUploadResponse getUploadId(String bucketName, String region, String objectName, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws Exception{
        return super.createMultipartUpload(bucketName, region, objectName, headers, extraQueryParams);

    }

    /**
     * 返回临时带签名、过期时间为1天的PUT请求方式的访问URL
     * @param bucketName  桶名
     * @param filePath    Oss文件路径
     * @param queryParams 查询参数
     * @return 临时带签名、过期时间为1天的PUT请求方式的访问URL
     */
    public String getPresignedObjectUrl(String bucketName, String filePath, Map<String, String> queryParams) throws Exception{
        return super.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.PUT)
                        .bucket(bucketName)
                        .object(filePath)
                        .expiry(1, TimeUnit.DAYS)
                        .extraQueryParams(queryParams)
                        .build());
    }

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

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

}

Controller

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

    @Autowired
    private MinioService minioService;

    @PostMapping("/getUploadUrls")
    @ApiModelProperty(value = "获取minio的uploadId及分片上传路径", notes = "获取minio的uploadId及分片上传路径")
    public MinioResponse createMultipartUpload(@RequestBody MinioDTO minioDTO) throws Exception{
            return minioService.createMultipartUpload(minioDTO);
    }

    @PostMapping("/merge")
    @ApiModelProperty(value = "合并切片视频", notes = "合并切片视频")
    public String completeMultipartUpload(@RequestBody MinioDTO minioDTO) throws Exception {
        return  minioService.completeMultipartUpload(minioDTO);
    }

}

具体的方法实现需要根据实际业务需求去开发,思路提供在这里

猜你喜欢

转载自blog.csdn.net/Alan_ran/article/details/129883169