excel文件中多个sheet单元格总分校验功能的实现

1,113 阅读7分钟

前言

最近在听一次需求宣讲会上,听业务部门的负责人对着后端同学讲:“你们研发同学能不能帮我们实现一下这个功能,具体来说就是一些地市分行汇总上来的一份Excel文件里有多个sheet表,第一个sheet表是汇总数据,其他sheet表是各分行数据,那么我们要做的工作就是仔细校验一下各分行每个单元格的数据相加是否等于总行出纳记录出来的数据,然后向上汇报给银监会。这个不能出一点差错,不然很麻烦,所以我们现在都是人工核对。我们从英国留学回来的硕士,每天就做这些工作浪费了大量时间,现在希望能借助科技的力量省一些时间,让他去做其他更有意义的事。期望你们能帮忙校验一下这些数据,对于出错了的单元格,背景展示为红色,没出错则显示正常。”现场展示Excel文件如下⏬ 特别说明 银行对信息安全的管理十分严格,故以上数据均为伪造数据
后端同学一看这个表格,表示能做是能做的,但是需要传上去的数据表头和表行不能这么复杂,需要是有规律的一行一行,一列一列的数据,像这样的他们不好做。我定睛一看,嘿!这功能前端完全能搞啊,而且不需要那么多他们说的表头表行限制的,没准是什么样校验完就能返回什么样。“悄悄地进村,打枪的不要。”,没有确切把握的事情就不声张了,会上没发声,会下搞起了预研,并成功的实现了这个需求。

实现原理

要想实现这样一个Excel文件中的多个sheet表格的总分校验功能,原理其实很简单,核心API就是使用XLSXreadFile()xlsx-style的设置样式功能。再结合node.js的writeFile,将其写入指定的文件中。

Excel的功能很强大,它的宏功能已经类似于程序运算了,也完全可以实现多张sheet表的元素在一张总表sheet内相加的功能,但它毕竟只能做一些辅助运算工作,对于比较分析几万条数据然后标记样式这种主观操作单靠软件自身还是无能为力的。还需要借助工具实现主观操作,提取里面有用的信息按照设定意图来进行分析比较,进而最终拿到预期结果。

xlsx

熟悉xlsx这个npm包的同学们应该都知道,它是前端处理Excel文件的利器,目前npm官网提供的是开源社区版本,增强的pro版本需要付费。这里我们不用需要Pro版本,把社区版弄明白就够用了。支持的输入输出格式有很多,比如csv/html/json,我就不贴官网的api了,有兴趣的自己去细看,主要记录下实现过程。踩了一些坑,也更加明白基础的重要性。

1.Parsing Workbooks(读取excel文件)

解析文件的第一步,是去读,这涉及到获取数据源并将其导入到库中,一些常见的场景是用node.js读,或者photoshop的扩展脚本,ajax请求、浏览器拓转或者浏览器的文件上传。 这里我们用第一种场景,使用node.js来读取文件,拿到数据源,进而进行分析校验。注意这里读的时候使用的type是buffer,方便后面设置样式的时候写入。之前使用其他类型也能正确读出来,但是设置样式无效。

const XLSX = require('xlsx');
const fs = require('fs');
const path = require('path');
const XLSXStyle = require('xlsx-style');
const wb = XLSX.readFile(path.resolve(__dirname, './test99.xls'), {type: "buffer"});

由于表格较大,只截取部分结果如下: 从SheetNames里可以观察到,读取到了原本隐藏的sheet,而从sheets里观察到,读取到了原本隐藏或者折叠行里的数据。这里我们并不需要关心隐藏的sheet表,只需要关注总计、分行一、分行二、分行三这四个sheet表来完成总分校验功能。 单独打印出总计表,截取部分结果如下: 这里每个单元格数据用js来描述其实就是一个对象,v原文是raw value,意指未加工的原始数据,t指的是type,那么n就是number,意思是他是个number类型的数据,w原文是formatted text,意指格式化后的文本,f原文是 cell formula encoded as an A1-style string (if applicable),按照我的理解即是原单元格所设定的公式。比如G9单元格的f是F9/C9,指的是在原sheet表中给这个单元格设定的公式。

2.Working with the Workbook(解析Excel文件)

根据官网文档,本段落主要讲如何去读一个具体的单元格、给Excel文件增加一个sheet表,从头开始创建一个新的工作簿。 但这都不是我们需要的功能,笔者是遍历要校验的单元格判断总分校验是否相等即可。
这里我们需要从C6检验到L32,由于都是伪造数据,都对应不上校验出来都是错误,故仅校验前三行(C6-L8)数据测试是否正确即可,校验代码如下: 注意点:

    1. 0.1 + 0.2 !== 0.3,这个大家都知道,因此需要将分行数据转为整数来相加。共有若干个分行数据,取到其中小数点位数最多的那一条数据max,每个数据与max相乘后相加不就能规避精度丢失的问题了?其实并不是,来看这个结果: 因此需要逐个数据判断有几位小数后取整,若批量取整然后再相加就不能拿到正确的结果了。
    1. Excel文件单元格有它自己的格式,因而你看到的数据并不一定是它的真实数据,来看这样一个单元格:它上面的格式是D9/C9,计算出来的结果是0.018490754622689,可实际呈现的是1.85%,也即0.0185,计算时应该按照0.0185来比对,可事实是计算机及程序是非常严谨的,它会无视单元格的格式限制,而采用0.018490754622689来进行计算,因此得出的比对结果是错误的。所以在上述比对代码中,若碰到小数位数多于4位的情况,我们就需要四舍五入来计算了。

3.Styling Workbooks(给sheet表设置样式)

找到有错误的单元格后,我们接下来就是要给其设置样式了。这里我们用到的是xlsx-style这个插件。

4.Writing Workbooks(写excel文件)

设置好样式之后,我们需要将它写入指定的Excel文件中,拿到结果。官方文档给了好多种写入的应用场景,这里我们采用node.js写入。

获取到结果

在当前项目根目录下获取到结果文件如下: 第七行是隐藏行,比对下来均错,意料之中。C6单元格故意写错,成功检测出来。后面再分别测试了百分比单元格,小数点单元格、除不尽单元格,均能拿到预期结果,就都不一一贴出来了。至此,完成了这个总分校验功能的实现。

想要通过上传下载来实现?

最核心的功能实现了,上传下载感觉都是辅助功能了。如果想通过上传源文件——>比对处理后——>下载,可以通过FileSaver来实现。具体步骤是使用express或者koa来实现一个伪后台,获取到上传的文件后进行解析,然后进行数据校验后,将文件buffer流传输给客户端浏览器,再通过FileSaver.saveAs方法来实现文件的保存。