当前位置: 首页 > news >正文

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

在上一篇文章中,我们讲解了分片上传的实现方式。在讲解断点续传之前,我要把上篇文章中留下的问题讲解一下。读过上一篇文章的小伙伴们都知道,对于分片上传来说,它的传输方式分为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;// 定义每块体积大小为5MBlet 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;// 定义每块体积大小为5MBlet 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();}

最后

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

相关文章:

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

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

Scala 和 Java在继承机制方面的区别

Scala 和 Java 都是面向对象编程语言&#xff0c;都支持类的继承机制。然而&#xff0c;尽管两者在基础概念上有很多相似之处&#xff0c;但在具体的实现和语法上&#xff0c;Scala 的继承机制有其独特之处。以下是 Scala 和 Java 在继承方面的一些主要区别&#xff1a; 多重继…...

spark sql上线前的调试工作实现

背景 每个公司应该都有大数据的平台的吧&#xff0c;平台的作用就是可以在上面执行各种spark sql以及定时任务&#xff0c;不过一般来说&#xff0c;由于这些spark sql的上线不经过测试&#xff0c;所以可能会影响到生产的数据&#xff0c;这种情况下大数据平台提供一个上线前…...

java -jar启动SpringBoot项目时配置文件加载位置与优先级

服务部署启动时,我们经常需要指定配置文件启动. 一般有四种,优先级如下 spring.config.location > spring.profiles.active > spring.config.additional-location > 默认的 application.yml 1.spring.config.location 外部配置文件优先级最高 一般配置文件在服务…...

每日一题 力扣LCP30.魔塔游戏

题目描述&#xff1a; 小扣当前位于魔塔游戏第一层&#xff0c;共有 N 个房间&#xff0c;编号为 0 ~ N-1。每个房间的补血道具/怪物对于血量影响记于数组 nums&#xff0c;其中正数表示道具补血数值&#xff0c;即血量增加对应数值&#xff1b;负数表示怪物造成伤害值&#x…...

iPhone搞机记录

-iPhone 8 或以上 设备进入DFU模式的方法&#xff1a; &#xff08;适用&#xff1a;iPhone 8/8 Plus、iPhone X 系列、iPad Pro3 (11-inch)/(12.9-inch)&#xff09; 1.保持设备处于开机或恢复模式下&#xff0c;插入数据线。 2.按一次设备的“音量加键”松开、再按一次“音量…...

Linux中共享内存(mmap函数的使用)

内存映射的基本使用 内存映射 概念&#xff1a; 使一个磁盘文件与内存中的一个缓冲区相映射&#xff0c;进程可以像访问普通内存一样对文件进行访问&#xff0c;不必再调用read,write。 mmap()的优点&#xff1a; 实现了用户空间和内核空间的高效交互方式 优化前&#xff1a;优…...

Golang与Erlang有什么差异

Golang和Erlang是两种备受关注的编程语言&#xff0c;它们各自具有独特的特点和优势。下面我将简单的探讨一下Golang和Erlang之间的差异&#xff0c;并且分析它们在并发模型、运行环境、函数式编程和领域特性等多个方面的不同之处。 并发模型 Golang使用goroutines和channels…...

cesium系列篇:Entity vs Primitive 源码解析(从Entity到Primitive)02

上篇文章中&#xff0c;我们介绍了使用viewer.entities.add添加entity之后的信号传递以及最后entity对象被传递到GeometryVisualizer&#xff1b; 这篇文章&#xff0c;我们则介绍如何在逐帧渲染的过程中根据GeometryVisualizer中的entity对象创建相应的primitive 这是下文中…...

golang windows 环境搭建 环境配置

golang windows 环境搭建 环境配置 Golang学习之路一环境搭建 MacBook Linux 树莓派raspberrypi安装Golang环境 官网下载地址: https://go.dev/dl/ https://golang.google.cn/dl/ 下载对应系统版本&#xff0c;例如windows 64位系统&#xff0c;下载&#xff1a;xxx.window…...

【Git】06 常用场景

文章目录 前言一、场景11.1 删除分支1.2 修改message信息1.2.1 最新一次commit的message1.2.2 过去commit的message 1.3 合并commit1.3.1 多个连续commit合并1.3.2 不连续commit合并 二、场景22.1 比较暂存区和HEAD所含文件的差异2.2 比较工作区和暂存区所含文件的差异2.3 将暂…...

docker下nacos(1.2.0)的持久化

一、创建数据库 运行以下代码自动创建数据库和表 CREATE DATABASE IF NOT EXISTS nacos_config /*!40100 DEFAULT CHARACTER SET utf8 */; USE nacos_config;SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for config_…...

Win32 SDK Gui编程系列之--弹出式菜单

1.弹出式菜单 例如,在命令提示窗口中点击鼠标右键,会出现如下图所示的弹出菜单(下拉菜单)。 这种弹出式菜单的实现很简单。不创建菜单栏,用CreatePopupMenu函数创建的菜单是最顶端的菜单就可以了。 菜单的显示使用TrackPopupMenu函数进行。 例如,点击鼠标右键显示弹出…...

VisaulStudio2022下用VB.net实现socket与西门子PLC进行通讯案例(优化版)

前言 对于电气工程师来说,不仅要会PLC,还要会上位机。 此前,我写过一个VB.net下雨西门子PLC通讯案例的博文: VisaulStudio2019下用VB.net实现socket与西门子PLC进行通讯案例 但当时很多东西都理解不深,博文也写的比较浅,但我看有不少收藏,也有些朋友在底下询问,所以,…...

npm安装命令

–save-dev   简写&#xff1a; -D 适用于各类loder , plugin&#xff0c; babel, webpack等 -save     简写 &#xff1a;-S 适用于 UI框架&#xff0c;vue等 1.npm install 包名 将包安装到 node_modules 目录&#xff0c;npm install 初始化时不会自动下载模块&#xf…...

【Git版本控制 01】基本操作

目录 一、初始配置 二、添加文件 三、查看日志 四、修改文件 五、版本回退 六、撤销修改 七、删除文件 一、初始配置 Git版本控制器&#xff1a;记录每次的修改以及版本迭代的一个管理系统。 # 初始化本地仓库&#xff1a;git init(base) [rootlocalhost gitcode]# gi…...

Spring 开发 pom.xml 配置文件(通用配置)

因为在打 jar 包时总会出现各种各样莫名其妙的问题&#xff0c;所以本篇博客提供了含有 Java8.0 &#xff0c;mybatis&#xff0c;mysql&#xff0c;lombok 以及打 jar 包的完整 pom.xml 配置文件&#xff0c;直接复制使用即可 <project xmlns"http://maven.apache.or…...

LabVIEW高精度主动模拟肺系统的开发与应用

在医疗设备的研发与测试中&#xff0c;高精度主动模拟肺系统扮演了不可或缺的角色。这种系统能够精确模拟人体的呼吸过程&#xff0c;对于呼吸机性能的测试、医疗人员的培训以及临床研究具有极其重要的意义。通过利用先进的硬件控制技术和软件算法&#xff0c;主动模拟肺系统能…...

打包 iOS 的 IPA 文件

目录 摘要 引言 准备 选择证书类型 创建应用程序 设置应用程序标识和证书 配置构建设置 构建应用程序 导出IPA 签名和导出 代码案例演示 完成 总结 参考资料 摘要 本篇博客将为您介绍如何打包iOS的IPA文件。从APP提交、创建应用程序、设置应用程序标识和证书、配…...

[Vulnhub靶机] DriftingBlues: 2

[Vulnhub靶机] DriftingBlues: 2靶机渗透思路及方法&#xff08;个人分享&#xff09; 靶机下载地址&#xff1a; https://download.vulnhub.com/driftingblues/driftingblues2.ova 靶机地址&#xff1a;192.168.67.21 攻击机地址&#xff1a;192.168.67.3 一、信息收集 1.…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...