vue+springboot+minio实现大文件分片上传
minio有自动进行分块的功能,将分块上传到Minio服务器,最后在进行合并。但是我们在使用时必须要将整个文件上传到系统,然后再调用Minio接口进行文件上传,当文件比较大时,就会占用太多宽带,从而大致上传慢,甚至使服务挂掉。因此我们需要进行优化。
在minio中提供了三个方法completeMultipartUpload,createMultipartUpload,listMultipart通过这三个方法我们可以将文件进行分片,分片后返回分片上传连接,等所有分片上传完成后,再进行分片合并,从而完成整个文件的上传。大致流程:
1、用户在前端使用vue-uploader上传文件,判断文件的大小。
2、如果文件大小小于5M(可以设置)则直接通过SpringBoot项目后端接口上传到minio服务器,如果大于5M时,对文件进行MD5编码,判断文件有没有上传过,上传过,就停止,没有上传,则计算分片数,调用SpringBoot的后端分片接口获取每个分片对应的上传地址到前端(这个地址是minio服务器的地址)。
3、前端根据分片计算每个分片的大小,将文件按大小进行分片,直接调用minio的分片地址将分片进行上传的minio服务器(注意,这一步没有经过SpringBoot项目后端接口)。
4、分片上传完成后,调用SpringBoot后端接口将分片进行合并,在服务器上形成文件。
详细流程,可以看下图
代码实现:
后端代码搭建:
数据库中建立表:
表的作用为:每当文件上传成功后,给表中插入一条文件相关的信息,每当上传文件的时候,查询该表,根据前端传递过来的文件的MD5字符串信息,判断文件有没有被上传过
DROP TABLE IF EXISTS `opu_sys_files`;
CREATE TABLE `opu_sys_files` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`bucket_name` varchar(100) NOT NULL COMMENT '桶名称',
`file_name` varchar(200) NOT NULL COMMENT '文件名称',
`suffix` varchar(8) NOT NULL COMMENT '文件后缀名',
`md5_code` varchar(32) NOT NULL COMMENT '加密md5编码',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统文件表';
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.woniuxy</groupId>
<artifactId>bigfileupload</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 鎼厤ctrl+F9 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.22</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</project>
application.yml
关键是mimio的配置,其他和以前一样
minio:
endpoint: http://localhost:9000
accessKey: minioadmin
accessSecrt: minioadmin
bucketName: gly1//这个好像没有用上
server:
port: 8081
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/testfile?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
typeAliasesPackage: com.gly.springcloud.entity
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
lazy-loading-enabled: true
aggressive-lazy-loading: false
mapper-locations: classpath:mapper/*.xml
pagehelper:
helperDialect: mysql
reasonable: true
minio配置类相关
MinioEndPointInfo:加载yaml中minio的信息
@Data
@ConfigurationProperties(prefix = "minio")
public class MinioEndPointInfo {
/**
* minio节点地址
*/
private String endpoint;
/**
* 登录用户名
*/
private String accessKey;
/**
* 密码
*/
private String accessSecrt;
private String bucketName;
}
PearMinioClient:MinioClientd的子类,重写了completeMultipartUpload、createMultipartUpload、listMultipart三个方法,并在重写的方法中调用了父类的这三个方法:
为什么要这么做:因为父类的三个方法的访问修饰符是protected,不方便在非子类中调用,重写后,将三个方法的访问修饰符全部弄成public,作用和父类三个方法的作用一样,但是可以在其他类中调用了
public class PearMinioClient extends MinioClient {
protected PearMinioClient(MinioClient client) {
super(client);
}
//completeMultipartUpload:合并分片
@Override
public ObjectWriteResponse completeMultipartUpload(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException<