react大文件上传

目录

大文件上传优点:

大文件上传缺点:

 大文件上传原理:

 为什么要用md5

实现流程:

部分代码1:

 部分代码2:​


大文件上传优点:

  1. 文件太大分片上传能加快上传速度,提高用户体验
  2. 能断点续传 如果上次上传失败或者中途离开的话下一次上传过的就不用重头开始了
  3. 已经上传过的文件根据HASH查询直接秒传

大文件上传缺点:

1.后台可能设置了请求时长限制,太久会上传失败(解决:后端不设置上传时长)

2.NGINX可能设置了文件上传的最大限制导致失败(解决:比如分片25M,nginx设置文件上传最大限度50M)

 大文件上传原理:

  1. 用户选择要上传的大文件,计算整个文件的MD5。
  2. 前端根据分片大小将文件切分成多个小块,计算每个分片文件的MD5。
  3. 逐个上传每个小块到服务器端。
  4. 服务器端接收并保存每个小块。
  5. 在服务器端,根据上传的小块将它们合并成完整的文件。

 为什么要用md5

        因为每个文件都会有自己专属独立的md5值,就像是每个人的身份证,比如我们在某个平台发布视频,将视频文件二次上传的时候就会遇到不容易过审的原因,同一个MD5就有很大的机率显示搬运被退回。刚好后端同学也可以通过MD5这种特性来判断上传的文件是否完整。

如何快速计算文件的 md5 值呢? 我们使用 js-spark-md5 这个库

实现流程:

         在upload组件上传文件的钩子函数beforeUpload() 中:

  1.  获取切片文件:设置切片文件大小、每次上传的开始字节,每次上传的结尾字节。文件切片的核心是使用Blob 对象的 slice 方法:
    var blob = file.slice([start [, end [, contentType]]]};
    start 和 end 代表 Blob 里的下标,表示被拷贝进新的 Blob 的字节的起始位置和结束位置。contentType 会给新的 Blob 赋予一个新的文档类型,很少使用。
  2.  计算分片文件的MD5
  3. 上传分片文件,断点续传(如何实现断点续传,关键点是后端需要记录文件文件切片的信息。用户在上传一个文件之前,先询问服务器,当前文件是否存在已经上传完毕的切片,如果存在的话,需要返回切片信息。前端根据返回的信息,调整当前的进度,上传未完成的切片)
  4. 检验分片数量及上传的结果,全部上传,文件合并
    1. 前端发送切片完成后,发送一个合并请求,后端收到请求后,将之前上传的切片文件合并。(上面展示的代码采用 这个)
    2. 后台记录切片文件上传数据,当后台检测到切片上传完成后,自动完成合并。
    3. 创建一个和源文件大小相同的文件,根据切片文件的起止位置直接将切片写入对应位置。

部分代码1:

export default class Project extends React.PureComponent {construction (props){this.state({file: {},fileplanNumber: 0, // 上传文件进度的百分比fileUploadState: '', // 上传文件的状态,fail失败, success成功interFaceStart: '', // 文件上传时的分片文件下标, 作用于断点续传})}beforeUpload = (file) => {this.setState({file,  // 把file存起来})let reader = new FileReader();let md5 = '';reader.onload = (event) => {const spark = new SparkMD5.ArrayBuffer;spark.append(e.target.value);md5 = spark.end();axios.post('', {md5,id, // 后端需要的}).then((res) => {const { fileId, start, finish, message } = res.data.data;if(res.data.code !== 200){message.error(message);}if(finish && finish === 'true'){this.setState({file: {}, // 等于true,表示上传完成了fileplanNumber: 0, // 上传文件进度的百分比})message.error(message);return;}// 请求成功后this.setState({fileId,interFaceStart: start, // 文件上传时,分片文件下标,为了断点续传}, () => {this.getSliceFile() // 获取文件切片})})}reader.readArrayBufffer(file);return false;
}// 获取文件切片
getSliceFile = async () => {const { search, cataList } = this.props;  const { file, fileId, interFaceStart } = this.state;const archiveId = cataList && cataList[cataList.length - 1].archiveId;const fileSize = file.size; // 文件的大小const piece = 1024 *1024 * 25; // 每片25Mlet start = 0; // 每次上传开始字节let index = 1;let end = start + piece; // 每次上传的结尾字节const chunksList = [];while(start < fileSize){const current = Math.min(end, fileSize) // 两者中取最小的const blob = file.slice.call(file, start, current);// 计算分片文件的MD5let sliceFileMD5 = '';sliceFileMD5 = await this.getSliceFileMD5(blob );// 拼接分片信息数据chunksList.push({file: blod,index,sliceFileMD5,});start = current;end = start + piece;index += 1;}// 检验分片数量,开始上传if (chunksList && chunksList.length) {const chunks = chunksList.slice(interFaceStart); // 分片上传的结果let resultList = 0;// 循环分片数据for(const item of chunks){// 调用接口上传分片内容const resultFile  = await this.uploadSliceFile(item, chunks);//记录上传结果resultList += 1;// 一次失败,结束后续上传if(!resultFile){break;}}// 检验分片数量,分片上传结果数量,全部上传完成,调用合并接口if(resultList === chunks.length){const { fileList } = this.state;axios.post('', { fileId,fileName: file.name,businessId: archiveId,businessType: 'archive',}).then((res) => {if(res.data.code !== 200){message.error(res.data.message)}message.success('上传成功!')// 调接口,刷新页面axios.post('', {pageNum: 1,pageSize: 10,archiveId,orderBy: '',sort: '',  }).then(res => {if(res.code !== 200){message.error(res.message)};})this.setState({file: {},fileId: '',fileUploadState: null,fileplanNumber: 0,fileList,interFaceStart: 0,});})} else {this.setState({fileUploadState: fail,});}}
}// 计算分片文件的MD5getSliceFileMD5 = (blob) => {return new Promise((resolve, reject) => {const sliceFileReader = new FileReader();let sliceFileMD5  = '';sliceFileReader.onerror = reject;sliceFileReader.onload = (event) => {const spark = new SparkMD5.ArrayBuffer();spark.append(event.target.result)sliceFileMD5 = spark.end();// 返回分片MD5resolve(sliceFileMD5);}sliceFileReader.readAsArrayBuffer(blob);})}// 上传分片文件uploadSliceFile = (fileMap, chunks) => {return new Promise((resolve, reject) => {const { sliceFileMD5, file, index } = fileMap;const fileBlob = new File([file], 'AAA.exe', { type: 'application/x-msdownload' })const { fileId } = this.state;const formData = new FormData();formData.append('fileId', fileId);formData.append('MD5', sliceFileMD5);formData.append('partSequence', index);formData.append('fileBlob', fileBlob);axios.post('', {formData,headers: {'Content-Type': 'multipart/form-data',}}).then(res => {if(res.data.code !== 200){message.error(res.data.message)return false;}const fileplanNumber = (index / chunks.length) * 100;this.setState({fileplanNumber,});resolve(true);})})}}

 部分代码2:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/206224.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue2中的props属性

vue2中的props属性 props传递&使用 调用子组件是创建子组件的实例 传递属性&#xff1a;:a"a"&#xff0c;非字符串都用v-bind created: 属性 状态 computed watch都处理好了 会被挂载到实例上get/set劫持&#xff1a;get获取最新&#xff0c;set目的抛出错误 修…

uniapp实现表单弹窗

uni.showModal({title: 删除账户,confirmColor:#3A3A3A,cancelColor:#999999,confirmText:确定,editable:true,//显示content:请输入“delete”删除账户,success: function (res) {console.log(res)if(res.confirm){if(res.contentdelete){console.log(123123123213)uni.setSto…

如何让Python2与Python3共存

安装 首先分别安装Py2和Py3&#xff0c;我都安装到C盘根目录里了&#xff0c;然后分别将Py2和Py3都配置到系统环境变量中去&#xff1a;C:\Python36\Scripts\;C:\Python36\;C:\Python27\;C:\Python27\Scripts; 配置 修改两个版本的可执行文件名字 验证 重新配置一下pip …

前端数组方法汇总集锦

前言 数组主要使用场景有&#xff1a; 存储和处理数据&#xff1a;数组是一种有序的数据结构&#xff0c;可以用来存储和处理多个相关的数据。在前端开发中&#xff0c;我们经常使用数组来存储和处理列表、表格、选项等数据。 循环和遍历&#xff1a;数组提供了循环和遍历的功能…

柱形图:制作图表时,有时会遇到柱形图系列没有居中显示,例如:

问题描述 制作图表时&#xff0c;有时会遇到柱形图系列没有居中显示&#xff0c;例如&#xff1a; 原因分析 柱形图的「分类」和「系列名」均选择了「地区」&#xff0c;导致分类下存在不同的系列&#xff0c;那么当前分类下没有的系列就会存在「空白占位」。 解决方案 此时…

vue diff算法原理以及v2v3的区别

diff算法简介 diff算法的目的是为了找到哪些节点发生了变化&#xff0c;哪些节点没有发生变化可以复用。如果用最传统的diff算法&#xff0c;如下图所示&#xff0c;每个节点都要遍历另一棵树上的所有节点做比较&#xff0c;这就是o(n^2)的复杂度&#xff0c;加上更新节点时的…

2023年中国高压驱动芯片分类、市场规模及发展趋势分析[图]

高压驱动芯片是一种能在高压环境下工作的集成电路&#xff0c;主要用于控制和驱动各种功率器件&#xff0c;如继电器、电磁阀、电机、变频器等。高压驱动芯片根据其输出电流的大小和形式可分为两类恒流型和开关型。 高压驱动芯片分类 资料来源&#xff1a;共研产业咨询&#x…

el-input限制输入整数等分析

文章目录 前言1、在 Vue 中&#xff0c;可以使用以下几种方式来限制 el-input 只能输入整数1.1 设置input 的 type为number1.2 使用inputmode1.3 使用自定义指令1.4 使用计算属性1.5 使用 onafterpaste ,onkeyup1.6 el-input-number 的precision属性 总结 前言 input 限制输入…

LangChain: 类似 Flask/FastAPI 之于 Django,LangServe 就是「LangChain 自己的 FastAPI」

原文&#xff1a;LangChain: 类似 Flask/FastAPI 之于 Django&#xff0c;LangServe 就是「LangChain 自己的 FastAPI」 - 知乎 当前阶段对开发者来讲&#xff0c;一个让人感到舒服的开发流程应该具备这么五特性&#xff1a; 支持 LCEL&#xff0c;让人赏心悦目又可以简化开发…

坚鹏:中国工商银行数字化转型发展现状与成功案例培训圆满结束

中国工商银行围绕“数字生态、数字资产、数字技术、数字基建、数字基因”五维布局&#xff0c;深入推进数字化转型&#xff0c;加快形成体系化、生态化实施路径&#xff0c;促进科技与业务加速融合&#xff0c;以“数字工行”建设推动“GBC”&#xff08;政务、企业、个人&…

Globalsign证书

Globalsign证书是一种被广泛应用于各个领域的网络安全解决方案。它提供了一系列的功能&#xff0c;包括保证在线交易的安全性、管理大量的数字身份以及自动验证和加密等。由于其全面的安全保障功能&#xff0c;许多大型公司、云服务供应商以及互联网创业者都选择了Globalsign证…

python命令行交互 引导用户输入一个数字

代码 以下代码将在命令行中&#xff0c;引导用户选择一个数字&#xff0c;并反馈用户输入的值 # -*- coding:UTF-8 -*- """ author: dyy contact: douyaoyuan126.com time: 2023/11/22 15:51 file: 引导用户输入一个数字.py desc: xxxxxx """#…