1,背景
在Form表单中上传文件,一般使用下面的流程,先上传文件到minio,然后提交表单数据。
注意:有些用户可能上传了文件而没有提交表单,得定期清理。
对于大文件上传,需要额外考虑上传中断问题(中断可能是因为网络抖动原因,也可能是文件太大,导致请求超时)
针对这个问题已经有比较成熟的方案了,就是分片上传。
2,分片上传
主要流程:
1)把大文件分隔成多个小文件(每个20M)
2)逐个/并发上传小文件
3)等所有小文件都上传完后,调用合并接口将多个小文件合并成大文件
前后端交互流程:
浏览器:我准备上传一个大文件,大小为1024MB,MD5为 abc321
后端:好的,请将大文件按20M/块进行分割,编号1-52的数据块都需要上传,上传代号为A
浏览器:这是代号A编号1的数据块,数据块MD5为xxx,请接收
后端:好的,接收完毕,MD5相同,数据块完整
浏览器:这是.....
后端:好的......
......中断......
浏览器:我准备上传一个大文件,大小为1024MB,MD5为 abc321
后端:好的,请将大文件按20M/块进行分割,编号30、36-52的数据块需要上传(其他部分之前上传过),上传代号为A
浏览器:这是代号A编号30的数据块,数据块MD5为xxx,请接收
后端:好的,接收完毕,MD5相同,数据块完整
浏览器:这是.....
后端:好的......
浏览器:数据块上传完毕,请将代号A的文件合并
后端:好的,合并成功
从上面的流程可以看出,分片上传主要需要3个接口,
1)开启一个上传任务接口,返回:切片大小,需要上传的分片编号,任务上传ID
2)上传分片接口
3)合并所有分片接口
那接下来我们看下Minio是否提供了这些接口
3,Minio的分片上传接口
MinioAsyncClient继承了S3Base类,提供了S3Base自带的分片上传能力(我这里用的是8.5.1版本,较早的版本可以在MinioClient类中找到类似的方法)
createMultipartUploadAsync // 创建文件上传任务
listMultipartUploadsAsync // 查询文件上传任务
uploadPartAsync // 上传分片
listPartsAsync // 获取分片上传列表
completeMultipartUploadAsync // 合并分片文件
注意:上面的方法都是protected方法,需要继承MinioAsyncClient这个类把这几个方法都开放出来。
如果需要实现分片和续传功能,还需要对这些接口进行封装
接口1:开启一个上传任务接口
- 需要前端先计算文件大小和文件MD5
- 后端根据文件MD5查询Minio中是否已经存在这个上传任务
- 有则再查询哪些分片已经上传,无则创建一个上传任务
- 根据一个固定的分片大小(例如20M)计算出分片块的数量
- 根据已上传分片编号列表,计算出待上传的分片编号列表
- 将上传任务ID、分片大小、待上传的分片编号列表返回给前端
接口2:上传分片接口
上传分片接口有两种方式,
1)后端中转,前端先将分片传给后端,后端再上传给Minio
2)前端直传,前端使用Minio的预上传Url(Presigned URL),直接将文件上传到Minio
Minio提供一种 Presigned Url ,把上传操作和认证操作分开,这样的好处是减少一次网络传输
如果使用这种方式,接口1中需要给每个待上传的分片获取一个PresignedURL(getPresignedObjectUrl方法),然后将这些URL全部返回给前端
接口3:合并所有分片接口
- 查询这个上传任务的所有分片
- 将所有分片合并成一个大文件
- 返回大文件的路径以及其他信息
这里主要是给出使用Minio实现分片上传的思路,有兴趣的小伙伴不妨自己动手实现一下。