前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue+element-ui+xlsx实现校验前端上传的Excel文件

vue+element-ui+xlsx实现校验前端上传的Excel文件

作者头像
RedSheep
修改2019-10-31 22:22:50
4.6K0
修改2019-10-31 22:22:50
举报

运行代码请看: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格式则会校验通过。

代码语言:javascript
复制
<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方法中,代码如下:

代码语言:javascript
复制
  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方法代码如下:

代码语言:javascript
复制
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,所以就导致校验异常。

优化的代码

  1. 相信绝大部分人都发现了问题,在readExcel方法中load相关的代码是异步执行的,并不会阻塞,所以此方法很快就执行结束了,结果什么也没返回,就直接导致了上面的问题,readExcelResultundefined,此时就开始搜异步转同步,果不其然,结果都是使用promise来实现。
  2. 再看到element-ui的官方文档看到了这么一句话: 看来只能是它了,来实现我所需要的功能。

微调后的代码

以下为beforeUpload改变后的代码:

代码语言:javascript
复制
  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改动后的代码

代码语言:javascript
复制
  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);
   });
 }

执行成功

以上代码完美解决了之前遇到的问题,能正确校验并返回了

总结

  1. 此时整个文件上传到文件校验整个过程就让人很舒服。不用等很久再响应,如果还是一个校验失败的结果,用户体验就下去了。
  2. 使用的Promise是关键,异步转同步,整个的lambda语法也让代码很容易理解,第一次使用,感觉尤为强大。
  3. 作为一个常年搞后端的人,偶尔解决一个自认为有点棘手的前端问题,感觉很有成就感,Mark下。

欢迎点评~~~~~~

觉得有帮助,欢迎关注
觉得有帮助,欢迎关注
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爪哇之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码历程
    • 失败的代码
      • 发现问题
    • 优化的代码
      • 微调后的代码
      • 执行成功
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档