springboot+easypoi大数据Excel导出
刚开始以为这个很简单,但是做的时候却遇到很多坑。特此记录一下,整理一下自己的解决方法,希望也可以帮助到其他人,如有不合理的地方,请望指出,不胜感激。
第一个坑:数据量过大,使用数据库查询(还是已分表的多表查询)拿取太慢了
这个很耗时,之前我以为通过数据优化的方式就可以解决,太年轻,数据量庞大,它的偏移量就是那么大,所以耗时还是无法解决。没办法,只能借助其他工具,因为我的导出数据是日志数据,即它不怎么变化,所以我使用了mongodb去存储我的数据。OK,数据拿取很快了。
第二个坑:内存泄漏
这个坑我是做之前就知道会存在的,毕竟那么大的数据量是很占服务器资源的,单个进程任务的数据量过大,而又无法及时回收到系统内存,最终导致系统内存耗尽而宕机。easypoi和mongodb都有相应的处理。
mongodb可以通过分页的方式去拿取数据,可以控制每次拿取的数据量:
嘿嘿,都分页了,所以这里还可以用多线程去做哦。
// pageSize 自定义取出大小
public Workbook excelWorkBook(Query query,Integer pageNum){
//分页拿取数据
Pageable pageable = new PageRequest(pageNum,pageSize);
query.with(pageable);
List<Test> dataList = mongoTemplate.find(query,Test.class,collectionName);
if (!CollectionUtils.isEmpty(dataList)){
workbook = ExcelUtil.exportBigExcel(params, Test.class, dataList);
}
logger.info("第"+pageNum+ "页的"+"的真实数据量大小为:"+ dataList.size());
if (!pageSize.equals(dataList.size())){
pageNum++;
workbook = excelWorkBook(query,pageNum);
}
return workbook;
}
public static Workbook exportBigExcel(ExportParams entity, Class<?> pojoClass, Collection<?> dataSet) {
//批量处理数据
ExcelBatchExportService batchService = ExcelBatchExportService.getExcelBatchExportService(entity, pojoClass);
return batchService.appendData(dataSet);
}
第三个坑:网关时间限制
哎,我这个服务是走了网关的,网关一直报超时,我当时就想把这个时间限制关了,但是这样做未免太不明智了,还好柳暗花明又一村,既然超时那我就骗过网关就好啦,自然而然,异步就是一个很好的解决方式。bingo,springboot还自带异步处理@Async,这篇博客里的讲的很不错,可以看下这个>https://www.cnblogs.com/huanzi-qch/p/11231041.html具体去实现。
很好很好,超时问题解决了,但是前端怎么拿到我的文件呢。因为这个服务已经用到了阿里云的OSS,所以我这里用的是OSS,也可以暂时放在服务器上。当后台把文件导出处理好了,并且上传到了服务器,可以通过websocket或者接口轮询的方式去通知前端可以去服务器上下载文件了。
// 上传
public void handleWorkBook(Workbook workbook,String fileName){
InputStream fileContent = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
workbook.write(bos);
byte[] barray = bos.toByteArray();
fileContent = new ByteArrayInputStream(barray);
} catch (IOException e) {
e.printStackTrace();
}
try {
//我这里是通过OSS存储文件的,用redis存储这个文件的过期时间
ossManageUtil.uploadFile(fileContent,fileName);
} catch (Exception e) {
e.printStackTrace();
}
}
这个文件是一次性的,可以通过redis的监视器去删除文件,要不然浪费存储空间资源。
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
@Autowired
private OSSManageUtil ossManageUtil;
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, byte[] pattern) {
String expiredKey = message.toString();
if(expiredKey.startsWith("ExportExcel_")) {
String key = expiredKey.replace("ExportExcel_", "");
try {
ossManageUtil.deleteFile(key);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
还有很多优化的点,几百万的数据量可以分文件再压缩或者多sheet导出,可以用多线程加快导出速度,可以用任务队列来控制这个并发等等。这些实现的过程可能又会遇到很多坑哦,哈哈。
下一篇: EasyPoi实现Excel导入导出