java Export大量数据导出和打包
作者:我是一颗小虎牙_
这篇文章主要为大家介绍了java Export大量数据的导出和打包实现过程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
项目需求
导出生成大批量数据的文件,一个Excel中最多存有五十万条数据,查询多余五十万的数据写多个Excel中。导出完成是生成的多个Excel文件打包压缩成zip,而后更新导出记录中的压缩文件路径。
大数据量文件一般采用异步生成文件,导出时首先授权生成一个流水号,而后将数据携带流水号请求导出接口。
抛开实际业务,做成一个比较公共的导出功能。
参数说明
{ "className": "ValideData", //导出的数据的实体类,类中有别名和顺序相关的注解 "createUser": "", //操作人 "downLoadNo": "202203181504732568468066304", //下载流水号 "fileName": "机卡绑定", //文件名 fileName+HHmmssSSS.xlsx "keys": [ //redis key的数据,分批获取数据 ], "remark": "机卡绑定", //备注(不关注) "type": "机卡绑定" //导出类型(不关注) }
坐标
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-compress</artifactId> <version>1.21</version> </dependency>
注:抛开导出前的参数校验,只关注导出操作 。
主要代码
逻辑说明:
- 导出前将请求参数更新到导出记录中。
- 类加载器加载需要导出数据的实体类
- 设置一个数据量指针,记录到每个文件的数据量
- 达到阈值时指定文件写出到磁盘并清缓。
- 重置数据量指针,新增一条文件记录(循环)
- 数据量指针未到阈值时但数据已经查询完成---->>写入剩余数据
- 查询该流水号的所有文件记录
- 压缩文件并返回压缩文件地址
- 更新到导出记录中
主流程
public void bigDataExport(PortDto dto) throws Exception { long start = System.currentTimeMillis(); log.info("开始导出,批次号:<{}>, 开始时间:{}", dto.getDownLoadNo(), DateUtil.now()); //修改导出记录 LambdaUpdateWrapper<PortDto> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(PortDto::getDownLoadNo, dto.getDownLoadNo()); //生成导出记录 int row = this.baseMapper.update(dto, updateWrapper); if (row > 0) { log.info("批次号:<{}>准备生成文件", dto.getDownLoadNo()); try { Iterator<String> iterator = keys.iterator(); Workbook workbook = null; ExportParams params = new ExportParams(); //加载导出数据实体类 Class<?> aClass = Class.forName(entityBasePackage + dto.getClassName()); int element = 0; while (iterator.hasNext()) { String key = iterator.next(); Collection<?> list = getList(key, aClass); element += list.size(); workbook = ExcelExportUtil.exportBigExcel(params, aClass, list); //文件数据达到阈值 if (element >= maxDataCount) { String fileName = dto.getFileName() + "_" + DateUtil.format(new Date(), "HHmmssSSS") + ".xlsx"; ExcelExportUtil.closeExportBigExcel(); FileOutputStream fos = new FileOutputStream(fileProp.getPath().getPath() + fileName); workbook.write(fos); fos.close(); element = 0; //更新地址 Map<String, Object> map = new HashMap<>(); map.put("downloadNo", dto.getDownLoadNo()); map.put("filePath", fileProp.getPath().getPath() + fileName); map.put("createTime", new Date()); this.baseMapper.insertPathRecord(map); log.info("文件写入完成,文件名:{}", fileName); continue; } iterator.remove(); } //写入剩余文件 if (element != 0) { String fileName = dto.getFileName() + "_" + DateUtil.format(new Date(), "HHmmssSSS") + ".xlsx"; ExcelExportUtil.closeExportBigExcel(); FileOutputStream fos = new FileOutputStream(fileProp.getPath().getPath() + fileName); workbook.write(fos); fos.close(); element = 0; //更新地址 Map<String, Object> map = new HashMap<>(); map.put("downloadNo", dto.getDownLoadNo()); map.put("filePath", fileProp.getPath().getPath() + fileName); map.put("createTime", new Date()); this.baseMapper.insertPathRecord(map); log.info("文件写入完成,文件名:{}", fileName); } long end = System.currentTimeMillis(); log.info("导出结束,批次号:<{}>, 结束时间:{}, 耗时:{}", dto.getDownLoadNo(), DateTime.of(end), DateUtil.formatBetween(end - start)); } catch (Exception e) { log.info("批次号<{}>导出异常:", dto.getDownLoadNo(), e); throw new BusinessException(""); } finally { log.info("批次号<{}>生成文件结束,准备压缩文件,修改状态", dto.getDownLoadNo()); //合并文件到导出文件记录主表 //当只有一个文件记录时直接更新主表文件地址 List<PortDto> recordList = exportDao.getPathRecord(dto); if (recordList.size() > 1) { //zipPath dto.setFilePath(zcat(dto, recordList)); } else { //xlsxPath dto.setFilePath(recordList.size()==0? "":recordList.get(0).getFilePath()); } updateWrapper.clear(); updateWrapper.set(PortDto::getFilePath, dto.getFilePath()); updateWrapper.set(PortDto::getSuccessTime, new Date()); updateWrapper.set(PortDto::getStatus, "1"); updateWrapper.eq(PortDto::getDownLoadNo, dto.getDownLoadNo()); this.baseMapper.update(null, updateWrapper); log.info("批次号<{}>更新下载记录表文件地址,修改状态成功", dto.getDownLoadNo()); } } }
文件压缩
/** * 多文件压缩 * @param dto 导出信息 * @Param recordList 文件路径 * @return void * @throws * @author Surpass * @date 2022/3/17 9:59 */ private String zcat(PortDto dto, List<PortDto> recordList) throws Exception { String fileName = dto.getFileName() + "_" + DateUtil.format(new Date(), "HHmmssSSS") + ".zip"; String zipPath = fileProp.getPath().getPath() + fileName; Archiver archiver = CompressUtil.createArchiver( CharsetUtil.CHARSET_UTF_8, ArchiveStreamFactory.ZIP, new File(zipPath) ); for (PortDto portDto : recordList) { archiver.add(FileUtil.file(portDto.getFilePath())); } archiver.finish(); archiver.close(); return zipPath; }
查询数据
/** * 查询redis数据 * @param key * @param cls * @return java.util.Collection<?> * @throws * @author Surpass * @date 2022/3/18 15:51 */ private Collection<?> getList(String key, Class<?> cls) { List<String> list = redis.getList(key); return list.stream().map(item -> JSONObject.parseObject(item, cls)).collect(Collectors.toList()); }
补充
导出还设置了队列计数器来限制同一时间最大的导出请求,使用aop在申请流水号时计数器+1,导出完成或者异常时队列计数器-1。导出完成后根据操作人发送邮件通知导出结果。
以上就是java Export大量数据导出和打包的详细内容,更多关于java Export数据导出打包的资料请关注脚本之家其它相关文章!