视频上传 - 断点续传那点事

在上一篇文章中,我们讲解了分片上传的实现方式。在讲解断点续传之前,我要把上篇文章中留下的问题讲解一下。读过上一篇文章的小伙伴们都知道,对于分片上传来说,它的传输方式分为2种,一种是按顺序传输,一种是并发的方式传输

上一篇里,我们只讲解了第一种方式,第二种方式并没有过多的去讲解。接下来,小编会带着大家,一步步拆解这个内容。大家系好安全带,面试题要来了。

如何实现分片的并发上传?

大家肯定都刷到过这样的面试题:给你100张图片,如何实现并发上传
首先我们要知道,浏览器一次性能发送多少个请求?答案是6个。那也就是说,我们需要设计一个流程,每次并发6个,直到所有的请求都完成响应。
那如何实现并发呢?

Promise似乎提供了一些方法,比如 Promise.allSettledPromise.allPromise.race

那这个时候,面试题又来了,它三的区别是什么在这个场景里,使用哪个API更合理?

共同点:参数都是由Promise实例组成的数组。

不同点:返回值不同。Promise.allSettled返回的是所有实例的状态数组。all与race返回的都是对象,最先成功或者失败的对象。

在我们这个场景里,我们优先选择 Promise.allSettled。知道了并发的方式还远远不够,假设我们有10个地址,一次并发发送6个请求,所以需要2次并发6个请求。那这个2次该如何得知呢?最简单也最实用的方案就是通过 Array.splice方法 来切割数组。因为splice进行删除操作时会影响到原数组,也就是原数组里也没有了被删除的元素。那我们就可以通过while循环来判断原数组是否还有剩余元素。没有剩余元素意味着并发请求结束了。

这个时候一个知识点也过来了。一起复习一下,slice与splice有什么区别?

  • 作用不一样。slice只能用来删除元素。splice可通过参数来决定删除或者添加元素的操作。
  • 对原数组的影响不一样。slice不会影响原数组。无论splice进行的是删除操作 还是 添加操作,它都会影响到原数组。

讲了这么多理论,也是时候动手实践一波了:

let arr = [
    {name: 1},
    {name: 2},
    {name: 3},
    {name: 4},
    {name: 5},
    {name: 6},
    {name: 7},
    {name: 8},
    {name: 9},
    {name: 10},
    {name: 11},
];

async function ax(){
    while(arr.length > 0){
        let newArr = arr.splice(0, 6);
        let result = await Promise.allSettled(
            newArr.map(item => {
                return new Promise((resolve, reject) => {
                    setTimeout(
                        () => {
                            return resolve(item.name)
                        },
                        1000
                    )
                })
            })
        );
        console.log('result是撒谎:', result);
    }
}

ax();

执行一下上述代码,我们会发现,按照了我们的预期,过了1s,控制台同时打印了1-6。再过了1s,控制台同时的打印了剩余元素。

那我们把这个思想转换到 分片上传的代码里。

// Video组件里的其他内容均不变,有不清晰的请看前2篇文章 ==========

// 并发分片上传
concurrencyUploadChunkFile = async () => {
    let self = this;
    // 定义每块体积大小为5MB
    let chunk_size = 5 * 1024 * 1024;
    // 当前上传的文件对象
    let fileObj = this.state.fileObj;
    // 当前上传的文件对象的体积
    let allSize = this.state.fileObj.size;
    // 当前文件被切割的总分片数量
    let allChunkCount = Math.ceil(allSize / chunk_size);
    
    // chunk文件集合
    let chunkArr = [];
    for (let index = 0; index < allChunkCount; index++){
        let startIndex = index * chunk_size;
        let endIndex = Math.min(startIndex + chunk_size, allSize);
        chunkArr.push({
            data: fileObj.slice(
                index * chunk_size,
                endIndex
            ),
            filename: `chunk-${index}`,
            chunkIndex: index
        });
    }

    // 开始并发分片上传
    while(chunkArr.length > 0){
        let reqArr = chunkArr.splice(0, 6);
        let reqArrFactory = reqArr.map(
            item => {
                return new Promise(
                    (resolve, reject) => {
                        let result = this.uploadChunkReq(item.data, item.chunkIndex, 'chunk');
                        return resolve(result);
                    }
                )
            }
        )
        let resArr = await Promise.allSettled(reqArrFactory);
        console.log('6个请求的响应如何:', resArr);
    }

    // 合并请求
    // this.mergeChunk();
    
    render(){
        return <div>
           <button onClick={this.concurrencyUploadChunkFile}>并发上传</button>
        </div>
    }

}

在上述代码中,我们定义了分片的体积为5M,此时假如我们分片上传了一个61M的视频,根据计算,会得到13个分片,如果我们控制网速,能明显的看到分层才对。因为毕竟发了3波,每波都是发送6个请求,每波之间都是按顺序的。就像下面这样:

在这里插入图片描述
并发上传分片到这里就结束啦。但如果想扩展的话,其实还有很多考题,就拿并发来举例子,大家可能还会听到一些考题,比如实现并发的方法有哪些?Promise.allSetlled是如何实现的等等。

我想说的是,大家不要去刻意背这些东西,因为这些题的本质就一个,就是js异步对象的运行机制。当你深入理解了就会发现,真的是举一反三。

断点续传

直到目前为止,大家有没有发现,我们进展的过于顺利,如果在上传分片的过程中出现了失误,我们应该怎么办?

断点续传是一个防错机制。它在大文件上传失败时,不必重新来过,从断掉的地方重新上传即可。

如何实现呢?很简单,从哪个分片断掉,就从哪块上传呗。

但是大家也会发现,即使是最简单的场景,根据每个项目实现分片上传的方式不同,断点续传的方法也不尽一样。因为断点续传的方式不一样,所以导致后端实现合并分片的方式也会随之改变。举个例子:

假如,我们有13个分片,序号分为是1-13,先并发1-6,此时我们发现分片6失败了,然后我们维护一个失败分片的数组,将分片6 push进去。接着并发 7 -12,此时都成功了。最后发送13,也成功了。因为分片6失败了,所以我们还需要再发送分片6,最后发现分片6也成功了。但是对于后端来说,在合并分片的时候就需要新增排序逻辑了,因为分片的序号对不上了。大家懂我的意思吧。

这里我们来新增前端处理断点续传的逻辑。逻辑如下:

  • 定义一个失败的分片集合
  • while循环时,通过判断原分片数组的长度是否为0,来确定所有的分片是否都上传了。
  • 在上传的过程中,如果有分片失败的,就将它push到 失败分片集合里。
  • 当原分片集合为空时,意味着此时应该重传失败的分片了。

// 其他内容不变 =======

// 并发上传分片
concurrencyUploadChunkFile = async () => {
    let self = this;
    // 定义每块体积大小为5MB
    let chunk_size = 5 * 1024 * 1024;
    // 当前上传的文件对象
    let fileObj = this.state.fileObj;
    // 当前上传的文件对象的体积
    let allSize = this.state.fileObj.size;
    let allChunkCount = Math.ceil(allSize / chunk_size);

    // 失败的分片集合(新增++++++++++++++++++++++++++++)
    let failedChunkArr = [];

    // chunk文件集合
    let chunkArr = [];
    
    for (let index = 0; index < allChunkCount; index++){
        let startIndex = index * chunk_size;
        let endIndex = Math.min(startIndex + chunk_size, allSize);
        chunkArr.push({
            data: fileObj.slice(
                index * chunk_size,
                endIndex
            ),
            filename: `chunk-${index}`,
            chunkIndex: index
        });
    }

    // 开始分片上传(update++++++++++++++++++++++++++++++++++++++++)
    while(chunkArr.length > 0 || failedChunkArr.length > 0){
        let reqArr = chunkArr.length > 0 ? chunkArr.splice(0, 6) : failedChunkArr.splice(0, 6);
        let reqArrFactory = reqArr.map(
            item => {
                return new Promise(
                    (resolve, reject) => {
                        let result = this.uploadChunkReq(item.data, item.chunkIndex, 'chunk');
                        if (result?.code != 200){
                            failedChunkArr.push(item);
                        }
                        return resolve(result);
                    }
                )
            }
        )
        let resArr = await Promise.allSettled(reqArrFactory);
        console.log('6个请求的响应如何:', resArr);
    }

    // 合并请求
    // this.mergeChunk();

}

最后

好啦,本篇文章到这里就结束啦,如果上述过程中有明显的错误,欢迎大家指正,希望我讲的对大家有帮助,那么我们下期再见啦,拜拜~~

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

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

相关文章

如何安装x11vnc并结合cpolar实现win远程桌面Deepin

文章目录 1. 安装x11vnc2. 本地远程连接测试3. Deepin安装Cpolar4. 配置公网远程地址5. 公网远程连接Deepin桌面6. 固定连接公网地址7. 固定公网地址连接测试 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站&#xff0c; 通俗易懂&#xff…

机器学习系列——(十六)回归模型的评估

引言 在机器学习领域&#xff0c;回归模型是一种预测连续数值输出的重要工具。无论是预测房价、股票价格还是天气温度&#xff0c;回归模型都扮演着不可或缺的角色。然而&#xff0c;构建模型只是第一步&#xff0c;评估模型的性能是确保模型准确性和泛化能力的关键环节。本文…

微信小程序解决华为手机保存图片到相册失败

1.新增隐私设置 2.优化代码 新增uni.authorize判断 _saveCode() {let that this;console.log(点击了保存图片)console.log(this.result)uni.authorize({scope: scope.writePhotosAlbum,success(e) {console.log(e)if (this.result ! "") {uni.saveImageToPhotosAlb…

github使用问题汇总

1. Permission denied 1.1. 问题描述 Permission denied (publickey). fatal: Could not read from remote repository. 1.2. 解决方法 生成公钥 ssh-keygen -t ed25519 -C "your_emailexample.com" 点击回车三次 Generating public/private ed25519 key pair. …

使用Nginx搭建旁路服务器获取客户端真实IP

一、前言 在实际业务开发过程中&#xff0c;很多时候有记录客户端真实IP的需求&#xff0c;但是从客户端发送的请求往往会经过很多代理服务器&#xff0c;导致后端服务获取的IP为代理以后的IP&#xff0c;不具有业务含义。为了解决这个问题&#xff0c;可以搭建一个旁路服务器…

上海亚商投顾:沪指涨超3% 深成指和创指双双飙涨超6%

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 今日A股三大指数一改近期低迷状态&#xff0c;早盘小幅低开后一路高歌猛进集体大涨&#xff0c;沪指涨超3%&am…

YUM | 包安装 | 管理

YUM 功能 软件包安装&#xff1a; 通过yum命令安装软件包。例如&#xff0c;安装一个名为 example-package 的软件包 yum install example-package更新包 检查更新&#xff1a; 检查可用更新&#xff1a; sudo yum check-update <package_name>软件包更新&#xff1a; y…

dolphinscheduler海豚调度(一)简介快速体验

1、简介 Apache DolphinScheduler 是一个分布式易扩展的可视化DAG工作流任务调度开源系统。适用于企业级场景&#xff0c;提供了一个可视化操作任务、工作流和全生命周期数据处理过程的解决方案。 Apache DolphinScheduler 旨在解决复杂的大数据任务依赖关系&#xff0c;并为应…

AI助力农作物自动采摘,基于DETR(DEtection TRansformer)开发构建作物生产场景下番茄采摘检测计数分析系统

去年十一那会无意间刷到一个视频展示的就是德国机械收割机非常高效自动化地24小时不间断地在超广阔的土地上采摘各种作物&#xff0c;专家设计出来了很多用于采摘不同农作物的大型机械&#xff0c;看着非常震撼&#xff0c;但是我们国内农业的发展还是相对比较滞后的&#xff0…

计算机视觉 | OpenCV 实现手势虚拟控制亮度和音量

Hi&#xff0c;大家好&#xff0c;我是半亩花海。在当今科技飞速发展的时代&#xff0c;我们身边充斥着各种智能设备&#xff0c;然而&#xff0c;如何更便捷地与这些设备进行交互却是一个不断被探索的课题。本文将主要介绍一个基于 OpenCV 的手势识别项目&#xff0c;通过手势…

基于Java学生管理系统设计与实现(源码+部署文档)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

解锁阿里巴巴面试题:创建线程的几种方式?

大家好,我是小米!今天我们来聊一个热门话题——阿里巴巴面试题:创建线程的几种方式。在技术的海洋中,线程是我们编程航程中的一艘不可或缺的船,驶向程序的未知领域。那么,究竟有哪些方式可以创建线程呢?让我们一起揭开这个技术的神秘面纱! 实现Runnable接口 首先,我…

最好的方式来预测未来是去创造它。

在辅导企业的过程中&#xff0c;对于「建设性的冲突」持开放态度&#xff0c;这背后反映了一种深刻的系统思考和变革管理的理念。在许多传统工作环境中&#xff0c;「和谐」往往被高度重视&#xff0c;但这种表面的和谐有时会掩盖问题的真相&#xff0c;阻碍组织的深层次变革和…

C语言:整形存储

#include<stdio.h> int main() {char a -1;signed char b -1;unsigned char c -1;printf("a%d,b%d,c%d", a, b, c);return 0; } b与a都是有符号数结果一样。a的signed相当于省略了。 运行结果 整形提升&#xff1a;整形算术运算总是以至少以缺省整型类型的精…

时序预测 | Matlab实现基于LSTM长短期记忆神经网络的电力负荷预测模型

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 时序预测 | Matlab实现基于LSTM长短期记忆神经网络的电力负荷预测模型 LSTM(长短期记忆)是一种递归神经网络(RNN)的变体,它在序列数据建模方面表现出色。电力负荷预测是一项重要的任务,可以利用LSTM神经网络…

Golang 学习(二)进阶使用

二、进阶使用 性能提升——协程 GoRoutine go f();一个 Go 线程上&#xff0c;可以起多个协程&#xff08;有独立的栈空间、共享程序堆空间、调度由用户控制&#xff09;主线程是一个物理线程&#xff0c;直接作用在 cpu 上的。是重量级的&#xff0c;非常耗费 cpu 资源。协…

从零开始手写mmo游戏从框架到爆炸(六)— 消息处理工厂

就好像门牌号一样&#xff0c;我们需要把消息路由到对应的楼栋和楼层&#xff0c;总不能像菜鸟一样让大家都来自己找数据吧。 首先这里我们参考了rabbitmq中的topic与tag模型&#xff0c;topic对应类&#xff0c;tag对应方法。 新增一个模块&#xff0c;专门记录路由eternity-…

Mac上几款好用的MacBook视频播放器

使用Mac电脑时&#xff0c;视频播放器可以说是我们使用频率最高的软件之一了&#xff0c;不管是工作时看视频资料还是在家里看下载好的电影&#xff0c;都需要用到视频播放器&#xff0c;本文中我们就来推荐几款好用的Macbook视频播放器&#xff0c;总有一款适合你&#xff01;…

完全免费,文字转语音、AI语音合成,视频配音就用这两款软件!

最近又有不少小伙伴找我要文字转语音、配音软件&#xff0c;刚好最近我也找了两款还比较不错的免费软件&#xff0c;今天就来分享给大家。最后还推荐了一款我自己一直在用的软件&#xff0c;建议认真看看&#xff01; 01 - Vpot-FREE&#xff08;电脑&#xff09; 它是一款永久…

灵敏可靠的缓激肽(Bradykinin)ELISA检测试剂盒

灵敏可靠的ELISA试剂盒&#xff0c;用于检测血浆、血清和尿液样本中的缓激肽 缓激肽&#xff08;Bradykinin&#xff09;于1949年被发现&#xff0c;由血浆中的球蛋白前体在蛋白酶的作用下生成。它的名字表明它会促使肠道缓慢运动。早在1909年&#xff0c;人们就注意到在尿液中…