vue+springboot多线程实现excel的上传和导出之心路历程

一、excel文件上传


前言

本篇文章没有具体的代码细节,仅仅是作者在开发时整个过程的大概记录。

如果感兴趣和必要的话,后续我也可以贴出代码供大家参看和指正。

技术:vue+springboot

1.1 前端

vue界面我使用的不熟悉,所以在编写前端代码时,我在csdn上参考了好多大佬的帖子,这里真心感谢。前端有各位大佬帖子的支持,顺利编写完毕。

1.2 后端

开始我选用poi的XSSFWorkbook来完成excel的文件上传。确定这个技术后,结合使用单线程的方式,完成了数据的导入。此时是比较初级的,没考虑内存啊效率啊等等。

接着我尝试使用easyExcel技术,该技术挺强的。它是基于磁盘操作从磁盘拿到数据,并且能一行一行的读取数据。因为这俩特性,在我看来一是解决了内存溢出问题二是解决了大数据量入库的问题。因为它是一行一行的读,我就能指定一次拿多少数据,然后批量入库,嗯,特别完美,所以我当时就开始研究它。

使用easyExcel时,遇到了第一个问题,依赖的一致性。easyExcel的依赖需要poi的依赖,而且有版本的指定。我当时使用2.2.7的版本,它指定poi的版本是4.1.7。这个好解决,一是把外层的poi依赖删除二是把外层的poi的版本置成4.1.7即可,第一个问题解决了。

马上就遇到第二个问题,这是个大问题,最关键的是我没有解决,所以后面就不得不放弃该技术了。这个问题是:NoClassDefFoundError:org/appache/poi/util/TempFileCreationStrategy。这个问题我看了不少csdn上的帖子,大佬们都讲的很详细,也提供了解决方法,但是都不适合我。其实这第二个问题就是第一个问题,解决方式也是第一个问题的解决方式,但是我这就是行不通,还是报错。而且通过依赖jar包,报错的该类是可以找到,maven也下载下来了,按理说能找到该类。所以我很无语,最终解决不了,就放弃了这个技术。

最后我回到poi的XSSFWorkbook,继续研究它。此时使用该技术加单线程已经实现,但是我们的数据量很大,20万起步。所以这里我又遇到第三个问题:一次性入库万级别的数据,数据库直接炸开,我的大脑当时也跟着爆炸。解决方式就是把万级别的数据在内存中做分批,一批1500(数量自己定)做批量入库,这样就好多了。在做分批时使用递归的方式,一段一段的分批往前拿,直至拿完,然后对每批的数据进行批量入库。这里有个小思路是不考虑内存了,一次性拿全部数据,然后递归分批做批量入库。注意这里一定要使用批量,不能单条操作,不然操作数据库的i/o开销会特别大,会直接干崩数据库。更不能查一条入库一条,更不可取。补充:拿的万级别的数据是从上传的excel中解析出来加载到内存的。在入库前,我们有个查询操作,对于不存在的数据要插入,对于存在的数据要更新。

解决第三个问题后,万级别的数据可以入库了,但是时间特别长,20万的数据需要20分钟,特别特别慢,我的领导直接就跳脚了,就差没大嘴巴子正反抽,所以我只能继续研究了。当时的思路有两个,一是拆文档,二是使用多线程。拆文档:也就是把一个大数据量的文档拆成一个个小数据量的文档。在进行一个一个的解析入库。恕我孤陋寡闻,我不知道文档可以咋拆,该方案果断被我pass了。

所以我选择了多线程。多线程的编写方式我也参考了csnd上各个大佬的帖子,真的很感谢。

使用多线程,将数据量进行拆分,每个线程获取部分数据进行处理。在使用多线程时,出现了一个小坑,就是自定义线程实现CallAble接口,它是带有返回值的,结果在跑多线程时只有第一个线程跑完了,后面的线程都不跑了,一时半会儿我找不到问题出在哪里,然后我尝试换成实现RunAble就可以,不知道是不是这个问题,希望大佬指正。

这里我要说明一点就是在使用多线程时,对每个线程下的数据依然保留了递归分批的处理。

20万的数据excel解析加入库:XSSFWorkbook+多线程(线程池)+线程内的递归分批处理+批量入库,耗时4分钟。

至此,excel文件上传完成。


二、excel文件导入


技术:vue+springboot

1.1 前端

vue界面我使用不熟悉,所以在编写前端代码时,在csdn上参考了好多大佬的帖子,这里真心感谢。

这里遇到一个问题:解析后返回值response要加载到new Blob()对象中,而不是response.data,不然会导致导出的文档打不开。返回response后,文档能打开,但是数据不对,显示的是[Object,Object]。这完全不对,这个问题我没有解决。

后面在vue中使用已有的utils的工具,把返回结果做了解析,就正常下载excel文档和打开,并且在文档中显示正常格式的数据,但此时的数据还有问题,这个问题在后端部分会继续说明。

1.2后端

使用poi的XSSFWorkbook来构建导出的excel文件。

这里的目的是要把excel文件写入到返回的response的流中,使生成的excel通过浏览器下载,在response的封装中,参考csdn上的大佬的就可以,这里真心感谢。

首先我使用的单线程的导出,数据量小时,没什么问题。

后来考虑到万级别的数据,我决定还是加入多线程的处理。我使用多线程导出数据到同一个excel中,这里遇到了第一个问题:把多线程的数据导入到一个sheet中,结果这个sheet只保存了一个线程的数据,其他线程的数据全部丢失,我找了一些csdn的帖子,发现这个问题无解(如果有大佬能解决这个问题,可以指点在下,在下感激不尽)。后来转变思路:还是加载同一个excel文档,但每个线程都生成自己的sheet并往里面放数据,这样就解决了数据丢失的问题。

第二个问题来了:有的线程生成的sheet里的数据出现条数不对的情况,而且数据发生混乱的问题。对于第二个问题,通过查看csdn大佬的帖子解决了。方式:算是是poi中的自己的问题,具体大家可以搜一下,这位大佬写的很详细,我就不赘叙了。简单说就是加锁,给poi中的某个对象加锁,并在setCellValue()设置值的地方加该锁,就能解决这个问题。这里补充一点,出现第二个问题时,我想到的就是加锁,但是加锁的对象使用的是我查询数据的对象,我换了不少位置(循环、判断、setCellValue()等)都加多锁,但是问题一直解决不了。

紧接着出现第三个问题:我们系统在selectList()获取集合数据时,对于查询结果有10000条的上限,这个上限值可以在配置中修改,但是我也不敢动,怕有影响。所以我选择分页查询,pageSize就设置10000,解决了这个问题。补充一点,这里的分页查询我是使用递归实现的。

走到这里,其实还有个思路问题,我是用分页查询和多线程完成excel导出。先说说我现在使用的思路:分页查询依次拿到全部数据加载到内存,然后在使用多线程。但当时我最开始想的思路是:在每批分页查询的同时结合多线程使用,但是这个行不通,因为每批数据量已经定死了就是10000条,然后对10000条进行多线程处理本身不太合理,而且是每批的分页查询的数据都做多线程,这更不合理,更重要的是无法把数据导入到excel中,因此pass了。

20万的数据导出excel:XSSFWorkbook(多线程使用同一个)+多线程(线程池)+线程内的分页查询(递归获取,加载到不同的sheet中),耗时2分钟。

至此excel的导出就完美结束了。


小结

实在是好久没写上传和下载的前后端代码了,实在生疏的很,在加上技术有限,就以上的这些东西,我花了足足一周的时间才完成。中间也是遇到各种大坑小坑,坑坑不一样,确实艰难。

这一周也着实让我花费了不少时间和心力。从接到需求到开发成功,没有人帮我出谋划策,虽然这对各位来说不难。根据实际的业务情况,要考虑到内存,效率,实用性,可用性等,结合着使用的技术来进行当前最优解的处理方式。

总的来说是顺利的,皆大欢喜!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值