前次的多线程导出太过于马虎,未成结局,一直在记忆里环绕,仿佛一颗钉子深深扎在心头,时间鞭策我,为了群友更好摸鱼,我痛定思痛,重复考虑,决定重开一篇。

串行导出紧缩流程

  1. 生成表头head
  2. 生成数据dataList
  3. 根据poi, EasyPoi, EasyExcel等东西输出到ByteArrayOutputStream
  4. ByteArrayOutputStream写入ZipOutputStream并紧缩
  5. 封闭流

调查上述流程,仔细考虑哪一步能变成并行,然后咱们从头梳理一下流程。

并行导出紧缩流程

  1. 生成表头head(串行)
  2. 分页生成数据dataList(并行)
  3. 循环分页成果集,根据poi, EasyPoi, EasyExcel等东西输出到ByteArrayOutputStream(并行)
  4. 循环分页成果集flush数据到ZipOutputStream(串行)
  5. 封闭流(串行)
从上面能够看出,导出的模型是,多线程读数据,单线程写数据到流。其间第2步和第3步能够兼并
有必要解说第1步,第4步,第5步为什么是串行

第1步和第5步不用多说,重点说第4步。简略而言便是数据写入到流的进程不是线程安全的,假如并行往流里写入数据,会发生原本线程 A 写的是,“张三,你胆敢摸鱼”,然后线程 B 写的是“老板娘!”,两个线程一起往流里边写入数据,成果输出了“张三,你胆敢摸老板娘!”这样的过错成果,所以第4步只能串行。这是个玩笑话,更多的时分会由于数据鸿沟过错导致反常。
明晰并行导出流程,下面咱们处理由于并行而引出的问题!

分页切开问题

咱们依照两个维度切开数据,文件和sheet,理想作用是根据导出数据总量得出文件数量 fileNum,根据sheetSize 核算每个文件sheet数量sheetNum,其间每一页sheet查一次数据库所得,能够得出sheet总数量 = fileNum * sheetNum,一起也是查询数据库的次数

多线程导出压缩方案

多线程导出压缩方案

文件和sheet顺序问题

上图是3个文件,每个文件有两个sheet(其间第3个文件只有一个sheet,是由于第6个sheet没有数据,没必要输出),sheetNo的编号从1到5,对应SQLlimit sheetNo, sheetSize,然后文件也是要从file1,file2,file3这样排序(文件名是url编码了)

导出进程反常

导出会输出多个文件,每个文件多个sheet,咱们不能由于某一个sheet,某个文件出错让整个导出进程失败,要把过错的原因写在sheet返回客户端

三个或许呈现反常的当地

  1. 查询数据,或许呈现业务反常,数据库衔接反常
  2. 根据导出东西写入流,或许会呈现反常,概率不大
  3. 流提前封闭了,或许由于服务器资源不行,导致流反常封闭,神仙也救不了

呈现反常,想办法弥补,不是从头查询数据,应该是友好把过错信息写到sheet,这样客户就能知道出错了,怎样出错了。

多线程导出压缩方案
上图是三个文件,第一个文件sheet-1的数据没问题,sheet-2由于读数据过错,在sheet输出load fial cause / by zore,这样用户就知道哪里有问题。假如由于反常整个文件或许整个sheet都不输出,客户底子没法知道导出的文件是否正常!

处理上面几个问题,导出功能根本明晰,但咱们是极客,怎样甘于平凡,所以我加了几个功能

导出中止

都多线程导出了,数据量肯定不小,几十万,几百万,几千万在那里导出,就看着进度条慢慢的涨,而你又等不下去了,把导出x停了,换个查询条件继续导出。由此可见,导出中止有必要。本教程咱们根据future实现中止导出线程,通过构建futureList,与导出日志ID绑定到map,需求中止的时分从map拿出futureList,遍历futureList,调用future.cancel()。相关常识需求futurejava中止线程,感兴趣的同学自行百度

断点续导

是的,有个名词叫断点续传,这两个功能是相同的,一个是能够暂停上传,一个是能够暂停下载。该计划需求导出文件到本地,根据RandomAccessFile实现。本教程根据web导出,实在是没有料呀,同学们感兴趣能够自己测验。

导出进度条

没得说,进度条能够是假的,但不能没有,根据redis做个导出进度条没毛病吧!

github

项目放在github,首要核心DefaultedExporterEasyExcelExecutor现已添加注释,不担心看不懂,clone项目还需求自己补充repository