vue+element-ui+xlsx实现校验前端上传的Excel文件
- 2019 年 10 月 5 日
- 筆記
运行代码请看:https://github.com/GloryXu/vue
背景
项目中需要校验上传的Excel模板是否符合规范。一开始的想法是在后端进行校验,但是后来想到一个跑批的文件最大是2M,如果放置在后端校验,对于不规范的文件,这2M的传输也就白费了,同时,对于用户的体验也很不好,就想把校验放置在客户端,不合乎规范的文件直接拒掉,节省带宽同时客户体验也有所改善。
代码历程
项目是使用vue+element-ui
构建的web项目,那么要实现前端Excel解析,就需要添加xlsx.js插件。 安装插件npm i xlsx --save
,当然也可以这样cnpm i xlsx --save
。
失败的代码
刚开始的使用element-ui
的代码如下,实现的上传文件功能。本例中以下Excel格式则会校验通过。
<el-upload style="display: inline; margin-left: 10px;margin-right: 10px;" action="#" ref="fileupload" :show-file-list="false" :http-request="upLoadChange" :before-upload="beforeUpload"> <el-button size="small" type="primary">上传文件<i class="el-icon-upload el-icon--right"></i></el-button> </el-upload>
将校验的代码放置在beforeUpload
方法中,代码如下:
beforeUpload(file) { // 读取Excel文件并校验返回Boolean值 let readExcelResult = this.readExcel(file); console.log(readExcelResult); if (readExcelResult) { const isLt2M = file.size / 1024 / 1024 < 2; if (!isLt2M) { this.$message.error('文件大小不能超过2MB!'); return false; } this.$message.success('校验成功!'); return true; } else { this.$message.error('校验文件失败'); return false; } }
readExcel
方法代码如下:
readExcel(file) {// 解析Excel let _this = this; const reader = new FileReader(); reader.onload = (e) => { try { // 以二进制流方式读取得到整份excel表格对象 var data = e.target.result, workbook = XLSX.read(data, {type: 'binary'}); } catch (e) { this.$message.error(e.message); return false; } // 表格的表格范围,可用于判断表头是否数量是否正确 var fromTo = ''; // 遍历每张表读取 for (var sheet in workbook.Sheets) { let sheetInfos = workbook.Sheets[sheet]; let locations = [];// A1,B1,C1... if (workbook.Sheets.hasOwnProperty(sheet)) { fromTo = sheetInfos['!ref'];// A1:B5 // 该方法获取每列第一行的值,如:输入A1:B5,返回['A1','B1'],该方法详情可查看文章末尾的github具体代码 locations = _this.getLocationsKeys(fromTo); } for (let i = 0;i < locations.length; i++) { let value = sheetInfos[locations[i]].v; if (value != i) { this.$message.error(locations[i] + ''s parameter isn't ' + i); return false; } } return true; } }; reader.readAsBinaryString(file); }
发现问题
代码看上去没问题,但是一直执行不通过,后来F12跟踪了下,发现readExcelResult
一直是undefined
,所以就导致校验异常。
优化的代码
- 相信绝大部分人都发现了问题,在
readExcel
方法中load相关的代码是异步执行的,并不会阻塞,所以此方法很快就执行结束了,结果什么也没返回,就直接导致了上面的问题,readExcelResult
为undefined
,此时就开始搜异步转同步,果不其然,结果都是使用promise
来实现。 - 再看到
element-ui
的官方文档看到了这么一句话: 看来只能是它了,来实现我所需要的功能。
微调后的代码
以下为beforeUpload
改变后的代码:
beforeUpload(file) { let _this = this; // 使返回的值变成Promise对象,如果校验不通过,则reject,校验通过,则resolve return new Promise(function(resolve, reject){ // readExcel方法也使用了Promise异步转同步,此处使用then对返回值进行处理 _this.readExcel(file).then(result => {// 此时标识校验成功,为resolve返回 const isLt2M = file.size / 1024 / 1024 < 2; if (!isLt2M) { _this.$message.error('文件大小不能超过2MB!'); } if (isLt2M && result){ resolve('校验成功!'); } else { reject(false); } }, error => {// 此时为校验失败,为reject返回 _this.$message.error(error); reject(false); }); }); }
以下为readExcel
改动后的代码
readExcel(file) {// 解析Excel let _this = this; return new Promise(function(resolve, reject){// 返回Promise对象 const reader = new FileReader(); reader.onload = (e) => {// 异步执行 try { // 以二进制流方式读取得到整份excel表格对象 var data = e.target.result, workbook = XLSX.read(data, {type: 'binary'}); } catch (e) { reject(e.message); } // 表格的表格范围,可用于判断表头是否数量是否正确 var fromTo = ''; // 遍历每张表读取 for (var sheet in workbook.Sheets) { let sheetInfos = workbook.Sheets[sheet]; let locations = [];// A1,B1,C1... if (workbook.Sheets.hasOwnProperty(sheet)) { fromTo = sheetInfos['!ref'];// A1:B5 locations = _this.getLocationsKeys(fromTo); } for (let i = 0;i < locations.length; i++) { let value = sheetInfos[locations[i]].v; if (value != i) {// 自定的校验规则,自由实现即可 // 校验失败reject reject(locations[i] + ''s parameter isn't ' + i); } } // 校验成功resolve resolve(true); } }; reader.readAsBinaryString(file); }); }
执行成功
以上代码完美解决了之前遇到的问题,能正确校验并返回了
总结
- 此时整个文件上传到文件校验整个过程就让人很舒服。不用等很久再响应,如果还是一个校验失败的结果,用户体验就下去了。
- 使用的
Promise
是关键,异步转同步,整个的lambda
语法也让代码很容易理解,第一次使用,感觉尤为强大。 - 作为一个常年搞后端的人,偶尔解决一个自认为有点棘手的前端问题,感觉很有成就感,Mark下。
欢迎点评~~~~~~