博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
后台系统上传文件回显上传进度条
阅读量:5974 次
发布时间:2019-06-19

本文共 6989 字,大约阅读时间需要 23 分钟。

hot3.png

 

大家有必要看到文末

xhr传统的AJAX传输对象,在做后台系统的时候经常遇到文件上传的情景,以往的xhr已经能够应对文件表格上传的功能,但如今遇到项目需求,上传的文件比较大数据表较多,为了提高用户体验有必要增加文件上传进度条,让用户知悉当前的操作进度,以免一直停留在空白页究竟有没有上传服务器都不清楚。此文用到的也还是AJAX的技术,XMLHttpRequest Level 2特性(也是老东西)

XHR2的事件例表:

attribute type Explanation
onloadstart loadstart When the request starts.
onprogress progress While loading and sending data.
onabort abort When the request has been aborted, either by invoking the abort() method or navigating away from the page.
onerror error When the request has failed.
onload load When the request has successfully completed.
ontimeout timeout When the author specified timeout has passed before the request could complete.
onloadend loadend When the request has completed, regardless of whether or not it was successful.

 

回顾AJAX基本知识

此文要做进度条,必须要在xhr.readyState === 3(解析,XMLHttpRequest对象开始持续读取服务器的响应)的状态中进行获取进度

 

组件结构

由于项目是基于Angular4开发,文件上传作为自定义组件进行封装,此文只解释如何获取进度情况,对于获取后的值如何回显到父组件等不作展开(父组件持续接受文件上传组件的进度情况,通过另外一个子组件去接收父组件接收到的进度情况并通过ngOnChanges(changes: SimpleChanges) 监听回显进度)

file-upload.component.html

 accept为H5新属性,当选择文件时默认只显示文件夹中的xls xlsx et后缀文件,当然也可以选择图片(image/png,image/gif)等。这仅是前端过滤文件选择用户最终还是能选择其他文件上传,入拖拽情况。

请避免使用该属性。应该在服务器端验证文件上传。

file-upload.component.ts

// 文件选择onFileSelect(event) {        // 选择需要过滤的excel文件后缀,前端过滤,不用服务器过滤        let pos = event.target.files[0].name.lastIndexOf(".");        let lastname = event.target.files[0].name.substring(pos + 1, event.target.files[0].name.length);        let hz = 'xls xlsx et';        if (hz.indexOf(lastname) >= 0) {            // dataTransfer兼容谷歌火狐文件拖拽上传,但没获取到该属性            this.files = event.dataTransfer ? event.dataTransfer.files : event.target.files;        } else {            this.files = undefined;        }}fileUpload(url: string) {        //先判断上传文件的格式是否正确        if (!this.files) {            this.mask.info("warn", "请上传正确的文件格式");            return;        }        // 进度百分比        let progress = 0;        // 通过formData做数据托管,避免做繁琐的字符串拼接地址        let xhr = new XMLHttpRequest(),            formData = new FormData();        formData.append('file', this.files[0], this.files[0].name);                // 监听upload事件的progress属性获取,进度百分比总长        xhr.upload.addEventListener('progress', (e: ProgressEvent) => {            if (e.lengthComputable) {                progress = Math.round((e.loaded * 100) / e.total);            }        }, false);        xhr.onreadystatechange = () => {            // 通过后台返回的百分比去进行回显页面进度条            if (xhr.readyState === 3) {                let responseObject = xhr.responseText.split("|");                let responseArr = [];                let latestRespone;                responseObject.forEach(val => {                    if (val && _.has(JSON.parse(val), 'percentage')) {                        latestRespone = JSON.parse(responseObject[xhr.responseText.split("|").length - 1]);                    }                    // 读取完百分比后把所有数据回显到界面上                    if (val && _.has(JSON.parse(val), 'errorObject')) {                        responseArr.push(JSON.parse(val)['errorObject']);                        latestRespone = { errorObject: responseArr };                        if (_.has(JSON.parse(val), 'success')) {                            latestRespone['success'] = JSON.parse(val)['success'];                            latestRespone['fail'] = JSON.parse(val)['fail'];                        }                        if (_.has(JSON.parse(val), 'filePath')) {                            latestRespone['filePath'] = JSON.parse(val)['filePath'];                        }                    }                })                this.progress.emit(latestRespone);            }            if (xhr.readyState === 4) {                progress = 0;                if (xhr.status >= 200 && xhr.status < 300) {                    /* 服务器返回的都是字符串,在这里需要做解析*/                    /* let rh = xhr.getResponseHeader('Content-Type');                    let result;                    if (rh.indexOf('json') !== -1) {                        result = JSON.parse(xhr.responseText);                    } else if (rh.indexOf('xml') !== -1) {                        result = xhr.responseXML;                    } else {                        result = xhr.responseText;                    }                    this.success.emit(result); */                } else {                    //上传失败                    //this.mask.info("error", "上传失败");                }            }        };        xhr.open('POST', url, true);        let jwt = localStorage["jwt"];        if (jwt) {            xhr.setRequestHeader("Authorization", "Bearer " + jwt);        }         xhr.send(formData);}

progress事件会在浏览器接收新数据期间周期性地触发。而onprogress事件处理程序会接收到一个event对象,其target属性是XHR对象,但包含着三个额外的属性:lengthComputable、loaded和total。其中,lengthComputable是一个表示进度信息是否可用的布尔值,loaded表示已经接收的字节数,total表示根据Content-Length响应头部确定的预期字节数。有了这些信息,我们就可以为用户创建一个进度指示器了。下面展示了为用户创建进度指示器的一个示例。

//get请求一般用来获取下载进度xhr.onprogress = function (event) {    var divStatus = document.getElementById("status");    if (event.lengthComputable) {        divStatus.innerHTML = "Recived" + event.loaded + " of " + event.total + " bytes";    }}//post一般用来获取上传进度xhr.upload.onprogress = function(e) {    if (e.lengthComputable) {        console.log(e.loaded / e.total * 100)    }}

总结:

看到这里,先跟大家说一声对不起这次的进度条并不是progress的产物,而是通过readyState===3解析后端返回的一条条数据进而回显进度百分比。这完全是后端做出来的进度条,目前这种方法也是能做到回显数据百分比显示。上面的xhr.upload.addEventListener('progress',...)仅是上传服务器的进度,此时并没有把服务器返回的数据带回来。这应该跟GET产生一个TCP数据包;POST产生两个TCP数据包有关系。

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

正常情况当服务器响应到结束只会发送一大段信息,至于为什么能在readyState === 3就能获取多条后台返回的数据,完全是后端代码一个专业名词,后端在解析完上传的文件后,每50条数据就会刷新一次,这样就可以达到前端接收每50条的进度情况,这摆明就是欺骗用户嘛。我发誓我是在了解上传文件进度条的时候才发现。

184039_LqRI_2949632.png184419_th3U_2949632.png184446_I6EB_2949632.png

由于整篇文章没有过多的xhr2的progress前端进度条,接下来说说progress这货怎么玩。

function uploadFile() {     var fd = new FormData();     fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);     var xhr = new XMLHttpRequest();     xhr.upload.addEventListener("progress", uploadProgress, false);     xhr.addEventListener("load", uploadComplete, false);     xhr.open("POST", "http://localhost:4000");//修改成自己的接口     xhr.send(fd);}function uploadProgress(evt) {     if (evt.lengthComputable) {        var percentComplete = Math.round(evt.loaded * 100 / evt.total);        document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%';     }     else {        document.getElementById('progressNumber').innerHTML = 'unable to compute';     }}function uploadComplete(evt) {     /* 服务器端返回响应时候触发event事件*/     alert(evt.target.responseText);}

在这里已经不是我们古老的AJAX post做法,通过readyState判断状态来获取服务器返回数据,这里是通过xhr提供的几个方法(看文首)load用来返回json集合。load事件可以完全替代xhr.readyState的1,2,3,4四个状态,evt.target可以看做是xhr的同类。post上传文件到服务器,通过progress确实可以返回上传速度,但可能是我本地服务器的原因不管是多大的文件都很快上传完全,进度条的作用不明显,在这里做不到像文首动图的效果,因为数据响应是一次过,根本没有间隔的余地,只有从0到1,不会出现0.1 0.2的数据。

上面代码还有一个好玩的地方,就是怎么快速搭建一个假的服务器,以往我们都用一些软件模拟本地服务器如(wampmanager),用 快速设置一个模拟后端,摒弃直接写假数据的低端做法。具体可以看

 

 

 

转载于:https://my.oschina.net/u/2949632/blog/1542001

你可能感兴趣的文章
[导入]让你的WAP网站有更好的兼容性
查看>>
.NET Exceptionless 本地部署踩坑记录
查看>>
TOMCAT 的 CONTEXT
查看>>
航电OJ-2544最短路
查看>>
CF772E Verifying Kingdom
查看>>
POJ 3417 Network
查看>>
雨林木风U盘装系统综合教程
查看>>
我们数学中常用的自然常数e代表什么?看完长知识了!
查看>>
V-by-one
查看>>
让我欲罢不能的node.js
查看>>
让AutoMapper更好用
查看>>
python3基础知识学习记录
查看>>
10年.NET老程序员推荐的7个开发类工具
查看>>
C#核心编程结构(2)
查看>>
rename设计思想(Perl版)
查看>>
第二次冲刺 第七天
查看>>
矩阵之矩阵乘法(转载)
查看>>
eclipse颜色主题插件(更改字体和背景的颜色)
查看>>
Python _内置函数3_45
查看>>
cf-Igor In the Museum (dfs)
查看>>