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导出,可以用多线程加快导出速度,可以用任务队列来控制这个并发等等。这些实现的过程可能又会遇到很多坑哦,哈哈。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于你的问题,我会为你详细介绍SpringBootEasyPoi如何实现数据导出。下面是实现步骤及代码示例: 步骤1:在pom.xml中添加EasyPoi依赖 ```xml <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.2.0</version> </dependency> ``` 步骤2:定义导出的实体类,并使用注解配置导出的列名和顺序 ```java public class User{ @Excel(name = "用户名", orderNum = "1") private String username; @Excel(name = "密码", orderNum = "2") private String password; // ...其他属性及getter/setter方法 } ``` 步骤3:编写导出Excel数据的控制器 ```java @RestController @RequestMapping("/api/user") public class UserController { @Autowired private UserService userService; @GetMapping("/export") public void exportData(@RequestParam(name = "fields") String fields, HttpServletResponse response) { // 处理表头列名 String[] titles = fields.split(","); // 查询数据库中所有用户信息 List<User> userList = userService.findAll(); // 根据用户选择的字段过滤掉不需要导出的列 for (int i = 0; i < titles.length; i++) { boolean isMatch = false; for (Field field : User.class.getDeclaredFields()) { if (field.isAnnotationPresent(Excel.class)) { Excel annotation = field.getAnnotation(Excel.class); if (annotation.name().equals(titles[i])) { isMatch = true; break; } } } if (!isMatch) { titles[i] = null; } } // 利用EasyPoi导出Excel ExportParams exportParams = new ExportParams("", "用户信息"); exportParams.setCreateHeadRows(true); exportParams.setHeadRows(1); Workbook workbook = ExcelExportUtil.exportExcel(exportParams, User.class, userList, null, titles, null); try (OutputStream outputStream = response.getOutputStream()) { response.setCharacterEncoding("UTF-8"); response.setHeader("Content-Disposition", "attachment;filename=" + java.net.URLEncoder.encode("用户信息.xlsx")); workbook.write(outputStream); } catch (IOException e) { e.printStackTrace(); } } } ``` 以上就是导出Excel的完整代码,其中针对用户选择需要导出的字段进行校验的部分,使用了反射机制获取注解信息进行匹配。为保证Excel格式的一致性,我们还使用了EasyPoi提供的ExportParams配置Excel表头等属性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值