上传文件显示进度条_文件上传带进度条进阶-断点续传

说明
1. 把文件按大小1M分割成N份
2. 每次上传时,告诉后台大文件的md5、当前第几份(从0开始)、总共几份
3. 并行上传,前端同时开启5个请求进行传输增加速度
4. 上传失败或出错后,继续上传下一份,把出错的份放在队尾,如果一直出错则中断请求防止死循环
5. 后台接受文件后通过md5进行比对,上次是否接受过此文件,如果接受则跳过,最后进行文件合并出来
6. 前端代码如下...
7. 查看源代码请点击在线演示地址

先上html

<input id="fileInput" type="file" multiple="multiple" name="" /><ul id="box"><!-- <li><span>文件名</span><span>文件类型</span><span>文件大小</span><span>上传进度</span><span>总进度</span><span>操作</span></li><li><span></span><span></span><span></span><span><i><em></em></i><i><em></em></i><i><em></em></i><i><em></em></i><i><em></em></i></span><span><i><em></em></i></span><span><a href="javascript:;">上传</a><a href="javascript:;">暂停</a><a href="javascript:;">删除</a></span></li> -->
</ul>

选择input时把文件信息写在页面上,因为涉及到断点续传,为保证文件的唯一性,需要本地读取文件并对其进行md5

fileInput.addEventListener('change', function() {var files = this.files;if (files.length) {let str = '<li><span>文件名</span><span>文件类型</span><span>文件大小</span><span>上传进度</span><span>总进度</span><span>操作</span></li>';for (var i = 0; i < files.length; i++) {var file = files[i];str += `<li><span>${file.name}</span><span>${file.type}</span><span>${formatByte(file.size)}</span><span><i><em></em></i><i><em></em></i><i><em></em></i><i><em></em></i><i><em></em></i></span><span><i><b>文件读取中</b></i><i><em></em></i></span><span data-index="${i}"><a data-control="1" href="javascript:;">上传</a><a data-control="2" href="javascript:;">暂停</a><a data-control="3" href="javascript:;">删除</a></span></li>`;}box.innerHTML = str;readFilesStep(0); // 文件太大,同步读取} else {box.innerHTML = ''}
}, false);

同步读取文件操作

function readFilesStep(i) {var files = fileInput.files;if (!files[i]) {return}var oLi = box.children[i + 1];oLi.dataset.count = Math.ceil(files[i].size / SIZE); // 总共多少份var readProgress = oLi.children[4].children[0].children[0];var reader = new FileReader();reader.readAsDataURL(files[i]);reader.onload = function () {oLi.dataset.md5 = md5(this.result);readProgress.innerHTML = '文件读取完毕';readProgress.parentNode.className = 'stop hide';readFilesStep(i + 1);reader = null;}reader.onerror = function (e) {console.error(e);readProgress.innerHTML = '文件读取失败,请重新选择';readFilesStep(i + 1);reader = null;}
}

因为li是创建出现的,对box进行事件委托

box.addEventListener('click', function (ev) {var target = ev.target;var control = target.dataset.control;var index = Number(target.parentNode.dataset.index);if (control === "1") {// 上传uploadItem(index);} else if (control === "2") {// 暂停pauseItem(index);} else if (control === "3") {// 删除delItem(index);}
}, false);

上传代码如下

var SIZE = 1024 * 1024; // 切片大小 
var FETCH_NUM = 5; // 上传文件同时发起请求数
var FETCH_MAP = {}; // 上传请求句柄,取消请求用
var FETCH_POOL = {}; // 每一个上传文件的份数function uploadItem(index) {var file = fileInput.files[index];var oLi = box.children[index + 1];var isPlaying = Number(oLi.dataset.playing) || 0;if (isPlaying) { return }oLi.dataset.playing = 1;var fileMd5 = oLi.dataset.md5;var count = oLi.dataset.count;var lastLoaded = Number(oLi.dataset.lastLoaded || 0);if (!fileMd5) {alert('请等待文件读取')} else {var maxErrorTimes = 10; // 最大出错次数// 第一次点开始会进行创建if (!FETCH_POOL[fileMd5]) {FETCH_POOL[fileMd5] = [];for (var i = 0; i < count; i++) {FETCH_POOL[fileMd5][i] = i;}}FETCH_MAP[fileMd5] = [];for (var i = 0; i < FETCH_NUM; i++) {FETCH_MAP[fileMd5][i] = null;step(i)}function step(i) {var cur = FETCH_POOL[fileMd5].shift();if (cur !== undefined) {FETCH_MAP[fileMd5][i] = uploadStep({file: file, cur: cur, count: count, md5: fileMd5,progressCb: function(e) {var loaded = lastLoaded + e.loaded;setProgress(loaded);var progressItem = (e.loaded / e.total * 100).toFixed(2) + '%';var oProgressItem = oLi.children[3].children[i];oProgressItem.title = progressItem;oProgressItem.children[0].style.width = progressItem;if (progressItem === '100.00%') {oProgressItem.className = 'stop'} else {oProgressItem.className = ''}}, successCb: function(){lastLoaded += SIZE;setProgress(lastLoaded, true);step(i);}, errorCb: function(status) {// 失败把当前份放在末尾,继续下一步FETCH_POOL[fileMd5].push(cur);if (status === 0) {// 手动取消 暂停} else if (maxErrorTimes--) {// 出错10次后不再上传,防止进入死循环step(i);}}});}}function setProgress(loaded, isFinished) {var oProgress = oLi.children[4].children[1];// 实际上传的数据大小 > 文件大小,此处做修正处理if (loaded > file.size) {if (isFinished) {loaded = file.size;oProgress.className = 'stop';} else {loaded = file.size * 0.9999;}}oLi.children[2].innerHTML = formatByte(loaded) + '/' + formatByte(file.size);var progress = (loaded / file.size * 100).toFixed(2) + '%';var lastProgress = oProgress.title || '0%';// 并行上传 此处可能是线路1的进度和线路2的进度比较,优先显示最大值progress = parseFloat(lastProgress) < parseFloat(progress) ? progress : lastProgress;// 总进度条oProgress.title = progress;oProgress.children[0].style.width = progress;if (isFinished) {oLi.dataset.lastLoaded = loaded;}}}
}

分步上传代码

function uploadStep(obj) {var file = obj.file;var cur = obj.cur;var fileMd5 = obj.md5;var count = obj.count;var progressCb = obj.progressCb;var successCb = obj.successCb;var errorCb = obj.errorCb;var params = new FormData();var filename = file.name;var fileChunk = file.slice(SIZE * cur, SIZE * (cur + 1));params.append('md5', fileMd5);params.append('file', fileChunk);params.append('cur', cur);params.append('count', count);var xhr = new XMLHttpRequest();xhr.onreadystatechange = function () {if (xhr.readyState == 4) {if (xhr.status == 200) {successCb && successCb(JSON.parse(xhr.responseText))} else {errorCb && errorCb(xhr.status)}}}xhr.upload.onprogress = function (e) {progressCb && progressCb(e)}xhr.open('POST', '/api/upload', true);xhr.send(params);return xhr;
}function formatByte(b) {var kb = b / 1024;if (kb >= 1024) {var m = kb / 1024;if (m >= 1024) {var g = m / 1024;return g.toFixed(2) + 'G';} else {return m.toFixed(2) + 'M';}} else {return kb.toFixed(2) + 'K';}
}

点击暂停时,取消上个建立的XMLHttpRequest请求,这里用FETCH_MAP[md5]进行标记

function pauseItem(index) {var file = fileInput.files[index];var oLi = box.children[index + 1];var isPlaying = Number(oLi.dataset.playing) || 0;if (!isPlaying) { return }oLi.dataset.playing = 0;var fileMd5 = oLi.dataset.md5;for (var i = 0; i < FETCH_MAP[fileMd5].length; i++) {if (FETCH_MAP[fileMd5][i]) {FETCH_MAP[fileMd5][i].abort();FETCH_MAP[fileMd5][i] = null;}}
}

点击删除时,取消上次请求,并隐藏li

function delItem(index) {var file = fileInput.files[index];var oLi = box.children[index + 1];var fileMd5 = oLi.dataset.md5;oLi.className = 'hide';if (FETCH_MAP[fileMd5]) {for (var i = 0; i < FETCH_MAP[fileMd5].length; i++) {if (FETCH_MAP[fileMd5][i]) {FETCH_MAP[fileMd5][i].abort();FETCH_MAP[fileMd5][i] = null;}}}
}

效果图:

126a415cdf5a7dd9c1114e2c150e11ce.png
https://www.zhihu.com/video/1086059444886044672

在线演示地址:上传大文件

Web Worker 真正的多线程上传,待更新。。。

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

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

相关文章

Unix 下的 vim 如何使用系统剪贴板

在 Unix 环境下&#xff0c;" 寄存器需要 xterm-clipboard feature 的 VIM 软件才能使用&#xff0c;具有这个 feature 的 VIM 可以安装 vim-gtk&#xff08;包含gvim和vim&#xff09;&#xff0c;使用 gvim 可以正常调用 " 寄存器。

python中关键字global的简单理解

python用global关键字来标识函数里或类里的全局变量&#xff0c;下面以例子来看看global关键字的作用。 未使用global关键字 a10 #全局变量 def sum(x):a2 #局部变量xa*xreturn x xsum(3) print("a:",a) #10 输出的是全局变量a10 print("x:",x) #6使用…

apache kafka_2018年机器学习趋势与Apache Kafka生态系统相结合

apache kafka在慕尼黑举行的OOP 2018大会上&#xff0c;我介绍了有关使用Apache Kafka生态系统和诸如TensorFlow&#xff0c;DeepLearning4J或H2O之类的深度学习框架构建可扩展&#xff0c;关键任务微服务的演讲的更新版本。 我想分享更新后的幻灯片&#xff0c;并讨论一些有关…

cookies丢失 同域名_后端设置Cookie前端跨域获取丢失问题(基于springboot实现)

1.跨域问题说明&#xff1a;后端域名为A.abc.com&#xff0c;前端域名为B.abc.com。2.后端设置一个cookie发送给前台&#xff0c;domain应该是setDomain(“abc.com”)&#xff0c;而不是setDomain(“B.abc.com”)3.另外&#xff0c;还要实现WebMvcConfigurerr配置加入Cors的跨域…

shell脚本--使用for循环逐行访问txt文件

方法1 export text_pathdata/1.txt for line in $(cat $text_path) doecho $line done方法2 export text_pathdata/1.txt for line in cat $text_path doecho $line done

vertx rest 跨域_Vertx编程风格:您的React式Web Companion REST API解释了

vertx rest 跨域Vertx提供了许多在轻量级环境中进行编程的选项&#xff0c;例如node.js。 但是&#xff0c;对于新用户来说&#xff0c;选择采用哪种方法来创建REST API几乎不会造成混淆。 在vertx中进行编程时&#xff0c;可以采用不同的模型。 下面通过易于理解的图表进行说…

输出节点位移_绝对值信号的编码器有哪些信号输出(一、二)

绝对值信号的编码器有哪些信号输出&#xff08;一、二&#xff09;之前介绍过很多次拉线位移传感器输出是有两大类的&#xff0c;数字信号输出和模拟量信号输出&#xff0c;而数字信号输出还分为增量型脉冲信号输出和绝对值信号输出&#xff0c;今天就系统的介绍一下绝对值信号…

vim 中的 quickfix 指令

用 quickfix 可以快速修改编译错误。 运行了 make 命令编译之后&#xff0c;如果有编译错误 Vim 会以列表形式把编译错误列出&#xff0c;并使用 quickfix 工具快速帮你定位出错的行。 指令说明cc显示编译错误的详细信息,这些信息显示在状态行里cn下一个编译错误cp前一个编译…

Python第三方库的安装,升级以及版本查看

方法&#xff1a;通过电脑的cmd命令行来进行python第三方库的安装&#xff0c;升级以及版本查看 安装和升级pip 安装pip方法1 在cmd命令行输入以下命令&#xff1a; python -m ensurepip #当提示不存在pip时使用这行代码进行安装安装pip方法2 在终端输入以下命令&#xf…

混合多云架构_混合多云每个人都应避免的3个陷阱(第1部分)

混合多云架构每天都在肆意宣传云&#xff0c;但每个人都应避免三个陷阱。 从云&#xff0c;混合云到混合多云&#xff0c;您都被告知这是确保业务数字化未来的一种方式。 您必须做出的这些选择不会排除提高客户体验和敏捷交付这些应用程序的日常工作。 让我们开始一段旅程&am…

vim 下的 ex 指令(底行命令模式下)

文章目录 (一)复制(二)文档光标移动(三)删除(四)粘贴(五)保存/退出(六)另存为文件/选取内容另存文件(七)查找/搜索字符(八)搜索指令 vimgrep(九)使用搜索指令 grep(十)文本行移动(十一)匹配模式替换(十二)显示行号(十三)normal 命令(十四)文件信息…

datastax.repo_使用Datastax Java驱动程序与Cassandra进行交互

datastax.repo今天&#xff0c;我这次返回了更多的Cassandra和Java集成&#xff0c;重点是使用Datastax Java驱动程序&#xff0c;而不是我已经写了很多文章的Spring Data Cassandra。 Spring Data实际上使用了Datastax驱动程序来与Cassandra进行交互&#xff0c;但是在它之上还…

beanshell断言_JMeter使用BeanShell断言

BeanShell简介BeanShell是使用Java语法的一套脚本语言&#xff0c;在JMeter的多种组件中都有BeanShell的身影&#xff0c;如&#xff1a;定时器&#xff1a;BeanShell Timer前置处理器&#xff1a;BeanShell PreProcessor采样器&#xff1a;BeanShell Sampler后置处理器&#x…

Mobaxterm常用的指令(基于linux)

列出所有的环境(3种方法) conda env list conda info --envs conda info -e创建虚拟环境(默认anaconda3/envs路径) conda create -n megumi python3.8.5 #megumi为环境名称,#3.8.5是安装python的版本激活环境(2种方法) source activate 环境名 conda activate 环境名退出环…

junit mockito_使用JUnit 5在Mockito中方便地进行模拟–官方方式

junit mockito从版本2.17.0开始&#xff0c;如果使用了JUnit 5&#xff0c; Mockito提供了官方&#xff08;内置&#xff09;支持来管理模拟生命周期。 入门 为了利用集成的优势&#xff0c;需要在JUnit 5的junit-platform-engine旁边添加Mockito的mockito-junit-jupiter依赖项…

正则表达式中的量词(限定符)含义的准确理解

量词准确理解?前面的元素&#xff08;pattern/子表达式&#xff09;匹配 0 次或者 1 次&#xff1b;出现 0 次或者 1 次。“次”改成“个”也行*前面的元素匹配大于等于 0 个前面的元素匹配大于等于 1 个{5}前面的元素匹配 5 个{2,6}前面的元素匹配 2 个到 6 个

docker初识_初识 docker 搭建自己的开发环境

换了一台设备&#xff0c;程序猿嘛第一件事肯定是先把开发环境搭建起来&#xff0c;以前为了方便都是搞的集成环境&#xff0c;这次准备下载集成环境的时候&#xff0c;突然想起 docker 这个东西&#xff0c;在下虽然了解一些概念性的东西但一直为曾实践。人嘛&#xff0c;没有…

PyTorch中使用指定的GPU

PyTorch默认使用从0开始的GPU&#xff0c;如果GPU0正在运行程序&#xff0c;需要指定其他GPU。 有如下两种方法来指定需要使用的GPU。 1.类似tensorflow指定GPU的方式&#xff0c;使用CUDA_VISIBLE_DEVICES。 1.1 直接终端中设定&#xff1a; CUDA_VISIBLE_DEVICES1 python…

jvm运行时类加载机制_JVM体系结构:JVM类加载器和运行时数据区

jvm运行时类加载机制各位读者好&#xff01; 在JVM系列的上一篇文章中&#xff0c;开发人员了解了Java虚拟机&#xff08;JVM&#xff09;及其体系结构。 本教程将帮助开发人员正确回答以下主题的问题&#xff1a; ClassLoader子系统 运行时数据区 1.简介 在继续之前&#x…

括号的分类

括号名称符号小括号&#xff08;parentheses&#xff0c;又称圆括号、括弧&#xff09;( )中括号&#xff08;square brackets&#xff0c;又称方括号&#xff09;[ ]大括号&#xff08;curly brackets&#xff0c; 又称花括号&#xff09;{ }六角括号〔 〕&#xff0c;中文才有…