说明
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;}}}
}

效果图:

https://www.zhihu.com/video/1086059444886044672

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

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

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

  1. Matlab GUI编程技巧(三):把figure文件显示到GUI的axe上

    在matlab的GUI编程中,把figure的图像显示到GUI的axe上是非常重要的的,GUI编程下的图像显示非常常见.matlab GUI默认菜单的保存图像默认为保存全部GUI,其中包括使用&quo ...

  2. php 拖拽 上传文件 进度,在Vue中如何实现带进度条的文件拖动上传功能

    这篇文章主要介绍了Vue实现带进度条的文件拖动上传功能,本文通过实例代码给大家介绍的非常详细,具有参考借鉴价值,需要的朋友可以参考下 1. 基本界面 content="width=devic ...

  3. 【文件上传绕过】——后端检测_文件的扩展名检测漏洞

    文章目录 一.漏洞说明: 二.工具: 三.实验环境: 四.实验目的: 五.检测方法: 1. 黑名单: 2. 白名单: 六.绕过方式: 1. 白名单绕过: 1.1 解析漏洞: 1.2 截断上传: 原理: ...

  4. php 蓝奏网盘上传文件,蓝奏云_文件上传_API

    ~~~[api] post:https://api-lanzou.anyhulian.ml/v2/upload.php *content-type=multipart/form-data; bound ...

  5. ftp服务器上图片文件显示不出来,ftp服务器上图片文件显示

    ftp服务器上图片文件显示 内容精选 换一换 安装传输工具在本地主机和Windows云服务器上分别安装数据传输工具,将文件上传到云服务器.例如QQ.exe.在本地主机和Windows云服务器上分别安装 ...

  6. c语言程序设计实验13文件,第13章_文件---《C语言程序设计》实验指导.ppt

    第13章_文件---<C语言程序设计>实验指导 第十三章 主要内容 13.1 C文件概述 13.2 文件类型指针 13.3 文件的打开与关闭 13.4 文件的读写 13.5 文件的定位 1 ...

  7. linux文件显示程序,Linux下文件显示命令简介

    文件操作,但是目录操作我们也是一样的.因为在Linux中,一切皆文件,目录也是文件.只不过目录文件是的文件内容是里面的文件名列表. 下面这些内容主要针对文件的文件内容操作.对于目录文件的内容操作有专门 ...

  8. php文件显示不完整,github文件显示不全

    github仓库里面有的文件显示不全,具体看这里,在编辑文件时就能看到全部内容 看了stackoverflow上类似的问题:Full file not displaying on GitHub?,然后 ...

  9. mfc打开一个.txt文件并进行处理_文件处理方法Python

    你好,我是goldsunC 让我们一起进步吧! 1. 文件说明 文件是存储在存储器上的数据序列,在计算机中,所有文件都是以二进制的方式进行存储的,而文件的展示形式一般分为两种:文本形式和二进制形式. ...

最新文章

  1. 图神经网络新课上架:​宾大2020秋季在线课程开课,视频上线B站
  2. iOS使用Charles(青花瓷)抓包并篡改返回数据图文详解
  3. 【BZOJ2819】Nim 树状数组+LCA
  4. C++学习系列笔记(三)
  5. ABP后台服务之作业调度Quartz.NET
  6. 牛客网暑期ACM多校训练营(第四场)G Maximum Mode(思维)
  7. Spark Mllib里相似度度量(基于余弦相似度计算不同用户之间相似性)(图文详解)...
  8. 《代码整洁之道》第14章 逐步改进 的代码片段
  9. 今天是元旦节,可是我失恋了
  10. python遍历字母_如何遍历字母表?
  11. MicroStation中关于mke文件的详解
  12. 基于springboot的校园食堂订餐系统
  13. 循环结构习题:公式求π值
  14. ISD9160学习笔记01_大联大Nuvoton ISD9160语音识别开发板初体验
  15. 对于目标识别的一些idea-传递特征的position而不是特征或特征图
  16. Linux使用uinput实现虚拟鼠标
  17. Bugzilla使用说明
  18. C++工资管理系统[2022-12-28]
  19. EASEUS Partition Master 调整Windows 7分区
  20. 关于 Glide 加载图片圆角问题

热门文章

  1. cucumber jvm_用Cucumber JVM编写BDD测试
  2. java编写应用程序_为您的Java应用程序编写数据驱动的测试
  3. 不可变集合相比可变集合_简单的基准测试:不可变集合VS持久集合
  4. java 文件流读取文本_如何在Java 8中处理流和读取文本文件
  5. Java 9、10及更高版本:Java平台的未来
  6. Java命令行界面(第18部分):JCLAP
  7. delayqueue_在DelayQueue中更改延迟,从而更改顺序
  8. java抽象类和模板模式_测试抽象类和模板方法模式
  9. 用Hamcrest验证DateTime和日期
  10. 具有Couchbase,Java EE和WildFly的CRUD Java应用程序