react大文件上传

news2025/1/15 6:17:25

目录

大文件上传优点:

大文件上传缺点:

 大文件上传原理:

 为什么要用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:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

纳米软件电源芯片测试案例分享:测试方案、仪器选型、解决测试难点

一、背景介绍 成都某半导体芯片公司是一家专注于开发设计半导体电源芯片的高新技术企业&#xff0c;目前企业对于电源管理芯片研发阶段的测试&#xff0c;绝大部分采用人工手动测试&#xff0c;效率低&#xff0c;耗时长&#xff0c;数据管理储存难度大&#xff0c;无法快速地完…

依托数据、平台、知识增强等优势 夸克大模型大幅降低问答幻觉率

“大模型时代&#xff0c;夸克有巨大机会创造出革新性搜索产品。”11月22日&#xff0c;夸克大模型公布了其面向搜索、生产力工具和资产管理助手的大模型技术布局。数据显示&#xff0c;夸克千亿级参数大模型登顶C-Eval和CMMLU两大权威榜单&#xff0c;夸克百亿级参数大模型同样…

SpringMVC问题

文章目录 SpringMVC运行流程MVC的概念与请求在MVC中的执行路径&#xff0c;ResponsBody注解的用途SpringMVC启动流程 SpringMVC运行流程 • 客户端&#xff08;浏览器&#xff09;发送请求&#xff0c;直接请求到 DispatcherServlet 。 • DispatcherServlet 根据请求信息调用 …

minio安装使用-linux

下载地址&#xff1a;MinIO | Code and downloads to create high performance object storage 选择 minio server 可以直接下载二进制文件。 将下载的文件传输到服务器的指定文件夹下&#xff0c;如 /opt/minio。 然后在&#xff0c;命令行启动minio&#xff1a; /opt/mini…

【Python】重磅!这本30w人都在看的Python数据分析畅销书更新了!

Python 语言极具吸引力。自从 1991 年诞生以来&#xff0c;Python 如今已经成为最受欢迎的解释型编程语言。 【文末送书】今天推荐一本Python领域优质数据分析书籍&#xff0c;这本30w人都在看的书&#xff0c;值得入手。 目录 作译者简介主要变动导读视频购书链接文末送书 pan…

常见面试题-Redis 主从复制原理以及痛点

Redis 主从复制如何同步数据呢&#xff1f; 参考文章&#xff1a;https://blog.csdn.net/Seky_fei/article/details/106877329 https://zhuanlan.zhihu.com/p/55532249 https://cloud.tencent.com/developer/article/2063597 https://xie.infoq.cn/article/4cffee02a2a12c2…

【Linux】-进程间通信-共享内存(SystemV),详解接口函数以及原理(使用管道处理同步互斥机制)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

Nuxt3使用

1.官网下载 npx nuxilatest init nuxt(有墙)&#xff0c;也可以到这个地方下载&#xff1a;git clone https://gitee.com/pmx600/nuxt3.git。 2.找到app.vue文件 将里面的代码修改为&#xff1a;<template><NuxtPage /></template>&#xff0c;NuxtPage 的…

【JavaScript】2.3 数据处理和函数式编程

文章目录 数组操作mapfilterreduce 函数式编程纯函数高阶函数闭包 总结 JavaScript提供了丰富的数据处理方法&#xff0c;特别是在数组操作和函数式编程方面。在这一章节中&#xff0c;我们将学习一些JavaScript中的数据处理技巧和函数式编程的基本概念。 数组操作 JavaScript…

替换的DLL用户电脑报错加载失败

编译后混淆加签名的dll 远程下载下来有个选项&#xff1a; 在某用户电脑上出现加载失败的报错 右键dll 属性里勾选解除锁定后 加载运行正常 跟用户电脑安全策略有关系 有的会出现 大部分不会

函数式编程-Stream流笔记-三更草堂

函数式编程-Stream流 1. 概述 1.1 为什么学&#xff1f; 能够看懂公司里的代码 大数量下处理集合效率高 代码可读性高 消灭嵌套地狱 //查询未成年作家的评分在70以上的书籍 由于数据中作家和书籍可能出现重复&#xff0c;需要进行去重 List<Book> bookList new Ar…

有用!2023汉字小达人市级比赛填空题专项训练,在线模拟题来了

只剩下一周了&#xff0c;2023年第十届汉字小达人市级比赛就要正式开始了。 敲黑板&#xff01;汉字小达人区级比赛时间为2023年11月30日&#xff08;星期四&#xff09;下午16&#xff1a;00-18&#xff1a;00&#xff0c;记得设置闹钟。提前和老师确认学校统一组织比赛&…

uni-app 使用uni.getLocation获取经纬度配合腾讯地图api获取当前地址

前言 最近在开发中需要根据经纬度获取当前位置信息&#xff0c;传递给后端&#xff0c;用来回显显示当前位置 查阅uni-app文档&#xff0c;发现uni.getLocation () 可以获取到经纬度&#xff0c;但是在小程序环境没有地址信息 思考怎么把经纬度换成地址&#xff0c;如果经纬度…

d3dx9_43.dll缺失怎么办?教你一分钟修复d3dx9_43.dll丢失问题

今天&#xff0c;与大家分享关于“d3dx9_43.dll丢失的5个解决方法”的主题。在我们的日常生活和工作中&#xff0c;我们可能会遇到各种各样的问题&#xff0c;而d3dx9_43.dll丢失就是其中之一。那么&#xff0c;什么是d3dx9_43.dll呢&#xff1f;它为什么会丢失&#xff1f;又该…

pikachu靶场PHP反序列化漏洞

pikachu靶场PHP反序列化漏洞 源码分析 查看源代码 class S{var $test "pikachu";function __construct(){echo $this->test;} }// O:1:"S":1:{s:4:"test";s:29:"<script>alert(xss)</script>";} $html; if(isset($_PO…

深度学习之基于YoloV5车辆和行人目标检测系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介YOLOv5 简介YOLOv5 特点 车辆和行人目标检测系统 二、功能三、系统四. 总结 一项目简介 # 深度学习之基于 YOLOv5 车辆和行人目标检测系统介绍 深度学习在…

帝国cms开发一个泛知识类的小程序的历程记录

#帝国cms小程序# 要开发一个泛知识类的小程序&#xff0c;要解决以下几个问题。 1。知识内容的分类。 2。知识内容的内容展示。 3。知识内容的价格设置。 4。用户体系&#xff0c;为简化用户的操作&#xff0c;在用户进行下载的时候&#xff0c;请用户输入手机号&#xff…

二、类与对象(二)

8 this指针 8.1 this指针的引入 我们先来定义一个日期的类Date&#xff1a; #include <iostream> using namespace std; class Date { public:void Init(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout << _year <&l…

【Java 进阶篇】Redis:打开缓存之门

介绍 Redis&#xff08;Remote Dictionary Server&#xff09;是一个高性能的键值对存储系统&#xff0c;被广泛用作缓存、消息中间件和数据库。它以其快速的读写能力、支持多种数据结构和丰富的功能而闻名。在这篇博客中&#xff0c;我们将深入了解Redis的概念、安装以及基本…

【LeetCode刷题】--43.字符串相乘

43.字符串相乘 方法一&#xff1a;做加法&#xff0c;模拟竖式乘法的方法计算乘积 class Solution {public String multiply(String num1, String num2) {if(num1.equals("0") || num2.equals("0")){return "0";}String res "0";//nu…