< JavaScript技术分享: 大文件切片上传 及 断点续传思路 >

news/2024/11/28 6:34:51/

在这里插入图片描述

文章目录

  • 👉 前言及含义
      • 切片上传
      • 断点续传
  • 👉 一、实现思路
  • 👉 二、使用场景
  • 👉 参考文献
  • 👉 伸手党福利: 即拿即用(前/后端思路均有)
  • 往期内容 💨


👉 前言及含义

在开发过程中,不管怎样简单的需求,在量级达到一定层次时,都会变得异常复杂。

就拿今天要说的文件上传来说,文件上传简单,但是当文件大小变得太大超出控制时,就会变得复杂了!

当上传大文件时,会存在以下几个变量会影响我们的用户体验

  1. 服务器处理数据的能力
  2. 上传时间会变长,高频次文件上传失败,失败后又需要重新上传等等
  3. 当遇到网络波动时,大文件上传容错率下降
  4. 上传文件请求超时

为了解决上述问题,我们需要对大文件上传单独处理

这里涉及到切片上传断点续传两个概念

切片上传

顾名思义,切片上传,就是将所要上传的大文件,按照一定的大小,将整个文件切割成多个数据块(Part)来进行分片上传。上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。

大致流程如下

  1. 将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
  2. 初始化一个分片上传任务,返回本次分片上传唯一标识;
  3. 按照一定的策略(串行或并行)发送各个分片数据块;
  4. 发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件

断点续传

断点续传,指的是在下载或上传时,将下载或上传任务人为的划分为几个部分

每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。用户可以节省时间,提高速度。现在常用的网盘下载工具和云存储平台大多采用了这种方式,可由用户手动暂停或继续文件上传、下载等操作。

一般实现方式有两种

  1. 服务器端返回,告知从哪开始
  2. 浏览器端自行处理

上传过程中将文件在服务器写为临时文件,等全部写完了(文件上传完),将此临时文件重命名为正式文件即可

如果中途上传中断过,下次上传的时候根据当前临时文件大小,作为在客户端读取文件的偏移量,从此位置继续读取文件数据块,上传到服务器从此偏移量继续写入文件即可。不过反复中断,小概率导致文件合并后,数据乱码的情况出现。

👉 一、实现思路

  1. 拿到文件,对文件进行fingerprint = md5(file),得到文件指纹。
  2. 将唯一标识指纹保存服务器。
  3. 切割文件,分段上传,每次上传一段。
  4. 服务器根据指纹进行索引判断文件上传进度,直到文件的全部片段上传完毕。

切片上传思路图
下面的内容都是伪代码(仅提供思路参考)
1. 读取文件内容:

// <input type="file" name="XXX" id="XXX" />
const input = document.querySelector('input')
input.addEventListener('change', function() {var file = this.files[0]
});

2. 可以使用md5, 实现文件的唯一性加密

const md5Code = md5(file)

3. 将文件进行切割,分成若干“块”小文件

// 创建一个reader对象, 允许操作file或blob
var reader = new FileReader()
// 用于启动读取指定的 Blob 或 File 内容
reader.readAsArrayBuffer(file)
// 当文件成功读取时,执行load 事件
reader.addEventListener("load", (e) => {//每10M切割一段,这里只做一个切割演示,实际切割需要根据实际文件大小,指定循环切割的片数和每片的大小...var slice = e.target.result.slice(0, 10*1024*1024)
});

FileReader相关理论知识 (点击跳转)

4.将切片逐片上传

const formData = new FormData();
// 与后端协商,看看如何界定上传片数。
// 如上面流程图所述,可以在第一片(尽量切小片一点,降低出错率)上传的时候,携带这个大文件对应的信息,如:整体裁切片数,文件总大小,文件名称及文件类型等等
//这里是有一个坑的,部分设备无法获取文件名称和文件类型,可以通过读取文件二进制流,解析文件类型,具体可以通过百度了解,不展开叙述。主要讲思路
formData.append('file-' + index, slice) // index 代表 此片的序号
formData.append('fileName', file.filename)
formData.append('fileType', file.fileType)
formData.append('md5Code', md5Code)// 》XMLHttp
var xhr = new XMLHttpRequest()
xhr.addEventListener('load', function() {//xhr.responseText
})
xhr.open('POST', '')
xhr.send(formData)
// 监听
xhr.addEventListener('progress', updateProgress)
xhr.upload.addEventListener('progress', updateProgress)function updateProgress(event) {if (event.lengthComputable) {//进度条}
}// 》 ajax
$.ajax({type:"post",url:"http://XXXXX",async:true,data:formData,cache:false,processData: false,contentType: false,xhr: function xhr() {//获取原生的xhr对象var xhr = $.ajaxSettings.xhr();if (xhr.upload) {//添加 progress 事件监听xhr.upload.addEventListener('progress', function (e) {//e.loaded 已上传文件字节数//e.total 文件总字节数var percentage = parseInt(e.loaded / e.total * 100)}, false)}return xhr},success:function(res){alert(JSON.stringify(res))}
})// 》 axios
let config = {headers:{'Content-Type':'multipart/form-data',},transformRequest: [function (data) {return data}],onUploadProgress: progressEvent => {//上传进度百分比let persent = (progressEvent.loaded / progressEvent.total * 100 | 0)console.log(persent)},
}
axios.post('http://xxxxxxxx/video/upload',formData,config
).then(response=>{var result = response.dataif(result.status == 0){console.log(result)}else{this.$message({message: '上传失败',type: 'error',duration:'1000'})}
}).catch(err => {console.log(err)
})

说明:e.loaded 为已上传文件字节数,e.total 为文件总字节数,可通过计算得出已上传字节数占比,读者可根据自己项目需要使用进度条或进度环

有了切割上传后,也就有了文件唯一标识信息,断点续传变成了后台的一个小小的逻辑判断

后端主要做的内容为:根据前端传给后台的md5值,到服务器磁盘查找是否有之前未完成的文件合并信息(也就是未完成的半成品文件切片),取到之后根据上传切片的数量,通过接口返回,告诉前端开始从第几节上传,继续进行上次未完成的文件上传

如果想要暂停切片的上传,可以使用XMLHttpRequest 的 abort 方法,其他请求同样有对应的暂停方法,可百度了解。

👉 二、使用场景

  • 大文件加速上传:当文件大小超过预期大小时,使用分片上传可实现并行上传多个 Part, 以加快上传速度
  • 网络环境较差:建议使用分片上传。当出现上传失败的时候,仅需重传失败的Part。而不会导致因网络问题,不断重新上传整个文件,提高容错率。
  • 流式上传:可以在需要上传的文件大小还不确定的情况下开始上传。这种场景在视频监控等行业应用中比较常见

👉 参考文献

js实现文件切片上传,断点续传
百度冲浪

👉 伸手党福利: 即拿即用(前/后端思路均有)

提供自掘金大佬:前端切片上传文件(可暂停),以链接形式返回

往期内容 💨

🔥 < 每日技巧: JavaScript代码优化 >

🔥 < 每日知识点:关于Javascript 精进小妙招 ( Js技巧 ) >

🔥 <Javascript技巧: Javascript 是个难泡的妞,学点技巧征服 “ 她 ” >

🔥 < 在element-ui中: 使用el-tree + el-table组件,联动请求用户数据表格组件 (基础版,后续可能更新) >


http://www.ppmy.cn/news/9362.html

相关文章

堆排序 TopK 优先级队列的部分源码 JAVA对象的比较

一.堆排序:我们该如何借助堆来对数组的内容来进行排序呢&#xff1f; 假设我们现在有一个数组&#xff0c;要求从小到大进行排序&#xff0c;我们是需要进行建立一个大堆还是建立一个小堆呢&#xff1f; 1)我的第一步的思路就是建立一个小堆&#xff0c;因为每一次堆顶上面的元…

SQL速算N日留存

之前才哥发布了《用SQL进行用户留存率计算》 链接&#xff1a;https://mp.weixin.qq.com/s/QJ8JUO00bVJe_K6sx_ttaw 简化数据后得到如下结构的数据&#xff1a; 由于用户和登录日期被设置为主键所以不需要再进行去重&#xff0c;下面看看如何快速求七日留存。 数据下载地址&…

Linux内核时间有关的API

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、延迟函数占用CPU的延迟:不占用CPU的延迟:单位换算二、获取时间点函数获得开机到现在总共的时间获得自1970年到现在时间三、定时器有关的函数初始化相关API时间拍转化API使用代码总结前言…

智能车|ROS主控与STM32建立通信软硬件全方位讲解

智能车|ROS主控与STM32建立通信软硬件全方位讲解前言智能车控制器功能通信内容硬件连接软件设置更新电平转换芯片的serial创建设备别名使用设备别名ROS与STM32串口通信代码ROS主控读取stm32发送的数据ROS主控向stm32发送数据前言 通常复杂的机器人会存在多个控制器&#xff0c;…

栈变量的作用域

C++自学精简教程 目录(必读) 栈变量的作用域就是栈变量的可见范围。 变量的作用域主要有下面几种常见的情况: 1 for循环作用域 变量只在for循环内部可见。 intmain(){//这里i不可见 for(inti=0;i<10;++i)//for循环内部可见 {cout<<

linux下常用调试技巧

1 linux下如何查看静态库和动态库都链接了那些库 1.1 静态库.a是没有指令可以看到其在生成过程中链接了那些库的 1.2 动态库.so可以通过ldd指令查看其在生成过程中链接了那些库 还有一种简单直观的方法,我们可以在编译过程中看到所生成的二进制文件,链接了那些库: 平时编译…

C++静态变量的使用训练

李太白年龄:80 加入列表后,最小的年龄现在是:80赵四天年龄:70 加入列表后,最小的年龄现在是:70王天霸年龄:90 加入列表后,最小的年龄现在是:70毛强年龄:20 加入列表后,最小的年龄现在是:20周瑜年龄:30 加入列表后,最小的年龄现在是:20万茜年龄:40 加入列表后,最小的年龄现在是:…

【 java 集合】List接口常用实现类对比以及ArrayList和LinkedList源码分析

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…