依星源码资源网,依星资源网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

【好消息,好消息,好消息】VIP会员可以发表文章赚积分啦 !
查看: 89|回复: 0

大文件秒传、断点续传和分片上传,包含Minio源码(go语言源码)和Spring-minio源码(JAVA源码)

[复制链接] 主动推送

1万

主题

1万

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
12016
发表于 2024-8-28 09:34:55 | 显示全部楼层 |阅读模式
大文件秒传、断点续传和分片上传,包含Minio源码(go语言源码)和Spring-minio源码(JAVA源码)
在日常业务中经常会遇到上传大文件的需求(如上传一部3G大小的高清电影资源),如果大文件资源上传不做特殊处理而直接使用小文件上传的方式上传到服务,可能会出现如网络不好导致上传一半就失败了,服务内存不够导致无法上传等等一些问题。那么这个特殊处理是什么处理呢?其实就是大文件的分片上传。

大文件秒传、断点续传和分片上传

大文件秒传、断点续传和分片上传


    大文件分片上传需要前后端协作来完成,前端的工作是切片和生成文件的唯一标识;后端的工作是接收文件的唯一标识,记录文件上传的分片信息和整合分片成完整文件。下面设计一套前后端协作方式将大文件分片上传到MinIO上的方案。

1、生成文件的MD5值

    前端需要唯一的标识一个文件,然后将唯一的标识传给后端做文件识别,那么用什么来唯一的标识一个文件呢?目前比较成熟的方案是将文件的二进制数据采用MD5映射成一个唯一标识。


大文件秒传、断点续传和分片上传

大文件秒传、断点续传和分片上传


    MD5的一个很大特点是文件内容有变动(即使在文件内容中加了一个空格)就会生成一个新的唯一标识。因此采用MD5的方式给文件生成一个唯一的标识。

    假设现在的文件有3G大小,那么计算其MD5值的时候直接将文件的内容的读取到内存中然后计算,此时内存可能会承受不了,所以采用分片的方式来计算MD5值。

大文件秒传、断点续传和分片上传

大文件秒传、断点续传和分片上传


核心代码:
  1. <template>
  2.     <div>
  3.         <input type="file" @change="onFileChange" />
  4.         <button @click="uploadFile">上传</button>
  5.     </div>
  6. </template>

  7. <script>
  8.     import SparkMD5 from 'spark-md5';

  9.     export default {
  10.         data() {
  11.             return {
  12.                 selectedFile: null,
  13.                 chunks: [],
  14.                 bytes: [],
  15.                 MD5Value: '',
  16.             };
  17.         },
  18.         methods: {
  19.             onFileChange(e) {
  20.                 this.selectedFile = e.target.files[0];
  21.                 this.chunks = this.createChunks(this.selectedFile, 100 * 1024);
  22.                 //计算文件的hash
  23.                 this.calculateHash(this.chunks);

  24.             },
  25.             createChunks(file, chunkSize) {    //文件切片
  26.                 const result = [];
  27.                 for (let i = 0; i < file.size; i += chunkSize) {
  28.                     result.push(file.slice(i, i + chunkSize))
  29.                 }
  30.                 return result;
  31.             },
  32.             calculateHash(chunks) {   //计算MD5值
  33.                 const spark = new SparkMD5();
  34.                 function readChunk(i) {
  35.                     if (i >= chunks.length) {
  36.                         this.MD5Value = spark.end();
  37.                         console.info(this.MD5Value);
  38.                         return;
  39.                     }
  40.                     let blob = chunks[i];
  41.                     const fileReader = new FileReader();
  42.                     //异步获取文件的字节信息
  43.                     fileReader.onload = e => {
  44.                         //获取到读取的字节数组
  45.                         spark.append(e.target.result);
  46.                         readChunk(i + 1);
  47.                     };
  48.                     //读文件的字节
  49.                     fileReader.readAsArrayBuffer(blob)
  50.                 }
  51.                 readChunk(0);
  52.             }
  53.         }
  54.     };
  55. </script>
复制代码
如果文件过大的话,即使采用分片的方式计算文件的MD5值也是非常慢的,所以设计的时候可以使用一个进度条的方式让用户知道当前正在解析文件并且当前的解析进度是多少,如下设计的解析文件的进度图:

大文件秒传、断点续传和分片上传

大文件秒传、断点续传和分片上传


2、判断当前的文件上传信息

    前端计算文件的MD5值后可以唯一标识这个文件,然后前端将MD5值传给后端,后端告诉前端当前的文件是否上传过

大文件秒传、断点续传和分片上传

大文件秒传、断点续传和分片上传


后端拿到MD5的值后到Redis中查询是否存在上传的记录信息:

(1)如果Redis中存在文件上传的信息,那么需要查询MinIO上已经上传成功的分片,计算未上传成功的分片信息,封装成对象返回给前端。

(2)如果Redis中没有数据的,查询数据库是否有文件的上传信息,数据库中要么文件已经完成上传并有文件在MinIO上的地址信息,要么就是没有上传(文件首次上传),结果封装成对象返回给前端。

核心代码:


  1. /**
  2. * 检查当前文件的上传情况
  3. *
  4. * @param md5 文件的md5标识
  5. */
  6. public FileUploadInfo checkFileUploadByMd5(String md5) {
  7.         //查询redis是否存在文件的上传信息
  8.         FileUploadInfo fileUploadInfo = RedisUtils.get(md5);
  9.         //Redis中存在上传信息
  10.         if (Objects.nonNull(fileUploadInfo)) {
  11.             //获取已经上传成功的分片信息
  12.             List<Integer> listParts = minioUtil.getListParts(fileUploadInfo.getObject(), fileUploadInfo.getUploadId());
  13.             fileUploadInfo.setListParts(listParts);
  14.             return fileUploadInfo;
  15.         }
  16.         //查询数据库是否有上传记录
  17.         File file = filesMapper.selectUpdaLoadByMd5(md5);
  18.         if (file != null) {
  19.             //标识文件之前已经上传成功了,直接返回其在MinIO上的地址
  20.             FileUploadInfo dbFileInfo = BeanUtils.copyProperties(file, FileUploadInfo.class);
  21.             return dbFileInfo;
  22.         }

  23.         return null;
  24.     }
复制代码
3、每个分片生成临时的凭证

大文件秒传、断点续传和分片上传

大文件秒传、断点续传和分片上传


    如果当前的文件是没有上传过或者断点上传的时候,需要携带md5和分片信息请求后端,后端根据MD5查询Redis中上传文件的信息来申请凭证,如果是断点续传情况,需要过滤已经上传成功的分片再去申请凭证;凭证通过后保存数据和更新Redis,然后返回凭证信息、uploadId给前端。

核心的代码:

  1. //文件分片申请凭证信息   
  2. public UploadUrlsVO multipartFileUpload(FileUploadInfo fileUploadInfo) {
  3.      UploadUrlsVO uploadUrlsVO;
  4.      String filePath;
  5.      //查询Redis是否存在上传信息
  6.      FileUploadInfo redisFileUploadInfo = RedisUtils.get(fileUploadInfo.getMd5());
  7.      //redis存在上传信息
  8.      if (Objects.nonNull(redisFileUploadInfo)) {
  9.          fileUploadInfo = redisFileUploadInfo;
  10.          filePath = redisFileUploadInfo.getObject();
  11.      } else {  //redis中无上传信息
  12.          //文件原始名称
  13.          String originName = fileUploadInfo.getOriginFileName();
  14.          filePath = DateUtil.format(LocalDateTime.now(), "yyyy/MM/dd") + "/"
  15.          + FileUtil.mainName(originName)
  16.           + "_" + fileUploadInfo.getMd5() + "."
  17.           + FileUtil.extName(originName);
  18.          fileUploadInfo.setObject(filePath).setType(suffix);
  19.       }
  20.         //未分片的文件上传
  21.         if (fileUploadInfo.getChunkCount() == 1) {
  22.             uploadUrlsVO = minioUtil.uploadSingleFile(fileUploadInfo.getContentType(), filePath);
  23.         } else {  // 分片上传
  24.             uploadUrlsVO = minioUtil.multiPartFileUpload(fileUploadInfo, filePath);
  25.         }
  26.         fileUploadInfo.setUploadId(urlsVO.getUploadId());
  27.         //最新的分片信息存到redis
  28.         RedisUtils.set(fileUploadInfo.getMd5(), fileUploadInfo, minioConfigInfo.getBreakpointTime(), TimeUnit.DAYS);
  29.         return uploadUrlsVO;
  30.     }
复制代码
4、前端上传分片和请求后端合并文件

大文件秒传、断点续传和分片上传

大文件秒传、断点续传和分片上传


    前端通过后端的凭证开始上传分片信息,分片上传完成之后请求后端将分片的合并成一个完整的文件,然后获取到完成的文件地址保存到数据库。

核心代码:

  1. @Override
  2. public String mergeMultipartUpload(String md5) {
  3.      //获取Redis中上传文件的信息
  4.      FileUploadInfo redisFileUploadInfo = RedisUtils.get(md5);
  5.      String fileUrl = StrUtil.format("{}/{}/{}", minioConfigInfo.getEndpoint(), minioConfigInfo.getBucket(), redisFileUploadInfo.getObject());
  6.     //组装数据库实体
  7.      Files file = BeanUtils.copyProperties(redisFileUploadInfo, Files.class);
  8.      file.setUrl(fileUrl);
  9.      file.setBucket(minioConfigInfo.getBucket());
  10.    
  11.       //分片为1时不需要合并,否则合并
  12.       if (redisFileUploadInfo.getChunkCount() == 1 ||
  13.           minioUtil.mergeMultipartFile(redisFileUploadInfo.getObject(), redisFileUploadInfo.getUploadId())) {
  14.             filesMapper.insertFile(file);
  15.             //删除Redis中分片的信息
  16.             redisUtil.del(md5);
  17.             return fileUrl;
  18.         }
  19.         //抛出异常提示
  20.        throw new BussinessException();
  21.     }
复制代码
总结:

(1)文件分片上传需要前端生成文件的唯一标识和分片。

(2)后端根据唯一标识判断是否存在上传信息,如果存在就判断是上传完成还是断点上传,如果上传完整直接返回Minio上文件的地址,如果是断点上传就返回哪些分片已经上传成功的信息给前端。

(3)前端过滤上传成功的分片,将未上传成功的分片请求后端申请凭证,申请成功之后上传分片,上传完成就请求后端合并分片成完成的文件并保存文件的地址到数据库,删除分片记录。

(4)秒传是因为之前这个文件已经上传过,数据库中已经存储了文件在MinIO上完整的地址。

(5)断点续传的原理是记录已经完成上传的分片,再次上传的时候这些分片无需再次上传,只上传未完成上传的分片。

Minio兼容Amason的S3分布式对象存储项目,采用Golang实现,客户端支持Java,Python,Javacript, Golang语言
附件中包含Minio源码(go语言源码)和Spring-minio源码(JAVA源码)
游客,本帖隐藏的内容需要积分高于 2 才可浏览,您当前积分为 0

提取码下载:
文件名称:提取码下载.txt 
下载次数:0  文件大小:13 Bytes  售价:39金钱 [记录]
下载权限: 不限 [购买VIP]   [充值]   [在线充值]   【VIP会员5折;永久VIP免费】
安全检测,请放心下载





相关帖子

扫码关注微信公众号,及时获取最新资源信息!下载附件优惠VIP会员5折;永久VIP免费
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

免责声明:
1、本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
2、本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,请勿任何商业目的与商业用途。
3、若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
4、论坛的所有内容都不保证其准确性,完整性,有效性,由于源码具有复制性,一经售出,概不退换。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
5、用户使用本网站必须遵守适用的法律法规,对于用户违法使用本站非法运营而引起的一切责任,由用户自行承担
6、本站所有资源来自互联网转载,版权归原著所有,用户访问和使用本站的条件是必须接受本站“免责声明”,如果不遵守,请勿访问或使用本网站
7、本站使用者因为违反本声明的规定而触犯中华人民共和国法律的,一切后果自己负责,本站不承担任何责任。
8、凡以任何方式登陆本网站或直接、间接使用本网站资料者,视为自愿接受本网站声明的约束。
9、本站以《2013 中华人民共和国计算机软件保护条例》第二章 “软件著作权” 第十七条为原则:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。若有学员需要商用本站资源,请务必联系版权方购买正版授权!
10、本网站如无意中侵犯了某个企业或个人的知识产权,请来信【站长信箱312337667@qq.com】告之,本站将立即删除。
郑重声明:
本站所有资源仅供用户本地电脑学习源代码的内含设计思想和原理,禁止任何其他用途!
本站所有资源、教程来自互联网转载,仅供学习交流,不得商业运营资源,不确保资源完整性,图片和资源仅供参考,不提供任何技术服务。
本站资源仅供本地编辑研究学习参考,禁止未经资源商正版授权参与任何商业行为,违法行为!如需商业请购买各资源商正版授权
本站仅收集资源,提供用户自学研究使用,本站不存在私自接受协助用户架设游戏或资源,非法运营资源行为。
 
在线客服
点击这里给我发消息 点击这里给我发消息 点击这里给我发消息
售前咨询热线
312337667

微信扫一扫,私享最新原创实用干货

QQ|免责声明|小黑屋|依星资源网 ( 鲁ICP备2021043233号-3 )|网站地图

GMT+8, 2024-11-24 01:49

Powered by Net188.com X3.4

邮箱:312337667@qq.com 客服QQ:312337667(工作时间:9:00~21:00)

快速回复 返回顶部 返回列表