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; // 每片25M

       let 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);
          
          // 计算分片文件的MD5
          let 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,
                });
            }
        }
}

    // 计算分片文件的MD5
    getSliceFileMD5 = (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();
                // 返回分片MD5
                resolve(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:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++:继承

一、继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保 持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象 程序设计的层次结构&#xff0c…

网络异常检测

随着社交网络、视频流、点对点技术、云计算和 SaaS 的出现&#xff0c;可以肯定地说&#xff0c;现代企业的好坏取决于他们的网络&#xff0c;尤其是在它们提供的带宽和安全性方面。无论是银行保护其数据免遭盗窃&#xff0c;还是商业组织保护其网络免受安全威胁和攻击&#xf…

Linux 中 .tar 和 tar.gz 的区别

1、前言 有时候你会发现&#xff0c;即便是有些拥有 3 年左右工作经验的运维或开发工程师对 .tar 和 .tar.gz 的区别并不是很清楚。.tar 和 .tar.gz 是在 Linux 系统中用于打包和压缩文件的两种常见格式。它们之间的主要区别在于压缩算法和文件扩展名。 2、区别 .tar .tar 是…

python练习题(markdown中的60道题)

1.Demo01 摄氏温度转化为华氏温度 celsius float(input(输入摄氏温度&#xff1a;)) fahrenheit (9/5)*celsius 32 print(%0.1f 摄氏温度转为华氏温度为 %0.1f % (celsius, fahrenheit))结果&#xff1a; 2.Demo02 计算圆柱体的体积 h, r map(float, input().split())# …

581. 最短无序连续子数组

581. 最短无序连续子数组 题目&#xff1a; 给你一个整数数组 nums &#xff0c;你需要找出一个 连续子数组 &#xff0c;如果对这个子数组进行升序排序&#xff0c;那么整个数组都会变为升序排序。 请你找出符合题意的 最短 子数组&#xff0c;并输出它的长度。 示例&…

【Android】声浪 UI 效果并附上详细代码

声浪效果是基于第三方实现的。 https://github.com/xfans/VoiceWaveView 将三方的 Kotlin 代码转 java 使用&#xff08;按照他的readme 进行依赖&#xff0c;好像少了点东西&#xff0c;至少本项目跑不起来&#xff09; 声浪效果在android 8 以上都是比较好的&#xff0c;不会…

【JavaEE】操作系统与进程

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JavaEE》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力)打造&…

Oracle的控制文件多路复用,控制文件备份,控制文件手工恢复

一.配置控制文件多路复用 1.查询Oracle的控制文件所在位置 SQL> select name from v$controlfile;NAME -------------------------------------------------------------------------------- /u01/app/oracle/oradata/orcl/control01.ctl /u01/app/oracle/fast_recovery_a…

规划类3d全景线上云展馆帮助企业轻松拓展海外市场

科技3D线上云展馆作为一种基于VR虚拟现实和互联网技术的新一代展览平台。可以在线上虚拟空间中模拟真实的展馆&#xff0c;让观众无需亲自到场&#xff0c;即可获得沉浸式的参观体验。通过这个展馆&#xff0c;您可以充分、全面、立体展示您的产品、服务以及各种创意作品&#…

实用篇 | T-SNE可视化工具详情及代码示例

本文主要是为了快速的了解t-sne和如何快速使用&#xff01; 简要了解TSNE TSNE&#xff0c;降维方法之一。降维在机器学习中非常重要。这是因为如果使用高维数据创建模型&#xff0c;则很容易欠拟合。换句话说&#xff0c;有太多无用的数据需要学习。可以通过从各种数据中仅…

Golang版本处理Skywalking Trace上报数据

Tips: 中间记录了解决问题的过程&#xff0c;如不感兴趣可直接跳至结尾 首先去es里查询skywalking trace的元数据 可以拿到一串base64加密后的data_binary(直接解密不能用&#xff0c;会有乱码&#xff0c;可参考https://github.com/apache/skywalking/issues/7423) 对data_b…

第四代智能井盖传感器:智能井盖位移怎么进行监测

井盖是城市基础设施的一个重要组成部分&#xff0c;若井盖出现移位等现象&#xff0c;可能会对路过的车辆和行人造成潜在危险。特别是那些含有甲烷气体的井盖&#xff0c;一旦气体超过阈值且被意外踩踏&#xff0c;可能会导致气体的释放&#xff0c;这便会引发一系列安全事故&a…

Linux wait函数用法

wait 函数是用于等待子进程结束并获取子进程的终止状态的系统调用。它在父进程中使用&#xff0c;用于等待其子进程终止并获得子进程的退出状态。 函数原型&#xff1a; pid_t wait(int *status);status 是一个指向整型的指针&#xff0c;用于存储子进程终止时的退出状态&…

clang+llvm多进程gdb调试

clangllvm多进程gdb调试 前言1. 命令行gdb2. 父进程调试3. 子进程调试4. 返回父进程 前言 在学习新增llvm的优化pass时&#xff0c;需要跟踪clang及llvm的调用栈。然而llvm通过posix_spawn()创建了新进程&#xff0c;这使得gdb调试必须有一定的技巧了。 1. 命令行gdb 以下命…

小红书干货类笔记怎么写?建议收藏

小红书干货类笔记是指在小红书这个社交平台上&#xff0c;用户分享的各种实用、有价值的生活技巧、经验、心得等内容的笔记。这类笔记通常具有以下特点&#xff1a;内容详实、实用性强、独特见解、图文并茂。 比如&#xff1a;某个妆要怎么化、某种技能该怎么学、某个城市该怎…

详细梳理山姆·奥特曼离职闹剧 仍试图重返OpenAI

大家好,我是极智视界,欢迎关注我的公众号,获取我的更多前沿科技分享 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码和资源下载,链接:https://t.zsxq.com/0aiNxERDq OpenAI 与微软围绕 Sam Altman 的离职风波,这场肥皂剧似乎还没到终结的样子。目前 …

跨越行业边界,CodeMeter护航AI领域安全与合规

在人工智能&#xff08;AI&#xff09;技术如ChatGPT的推动下&#xff0c;工业视觉、医疗诊断和智能驾驶等领域正在经历重大变革。这些技术不仅扩大了应用范围&#xff0c;也带来了数据安全、软件授权保护和合规性等新挑战。 AI工业视觉正在推动制造和自动化的快速发展&#x…

YOLOv5结合华为诺亚VanillaNet Block模块

🗝️YOLOv5实战宝典--星级指南:从入门到精通,您不可错过的技巧   -- 聚焦于YOLO的 最新版本, 对颈部网络改进、添加局部注意力、增加检测头部,实测涨点 💡 深入浅出YOLOv5:我的专业笔记与技术总结   -- YOLOv5轻松上手, 适用技术小白,文章代码齐全,仅需 …

出海企业首选的免费开源财务管理系统解决方案

计费与订阅管理 Odoo计费与订阅管理解决方案可帮助您同步从订单、计费到收入确认的复杂流程 Odoo Subscriptions将计费与订阅置于核心业务流程中&#xff0c;将其从普通的后端功能转化为具有决定性意义的战略性业务工具。Odoo统一计费框架支持根据事务、订阅、使用量计费以及…

大数据题目的解题技巧

目录 大数据题目的技巧总括 实例精析 实例一 实例二 实例三 大数据题目的技巧总括 &#xff08;1&#xff09;哈希函数可以把数据按照种类均匀分流&#xff1b; &#xff08;2&#xff09;布隆过滤器用于集合的建立与查询&#xff0c;并可以节省大量空间&#xff1b; &…