easyExcel实现分批导入,动态表头分批导出,以及导出表格样式设置
- 游戏开发
- 2025-08-12 06:21:02

<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.6</version> </dependency> 一,分批导入 1.首先配置表格头映射类 @Getter @Setter @EqualsAndHashCode public class IndexOrNameData { /** * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配 */ @ExcelProperty(index = 2) private Double doubleData; /** * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据 */ @ExcelProperty("字符串标题") private String string; @ExcelProperty("日期标题") private Date date; } 2.编写excel数据读监听器 // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 @Slf4j public class DemoDataListener implements ReadListener<IndexOrNameData > { /** * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 */ private static final int BATCH_COUNT = 100; /** * 缓存的数据 */ private List<IndexOrNameData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); /** * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。 */ private DemoDAO demoDAO; public DemoDataListener() { // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数 demoDAO = new DemoDAO(); } /** * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来 * * @param demoDAO */ public DemoDataListener(DemoDAO demoDAO) { this.demoDAO = demoDAO; } /** * 这个每一条数据解析都会来调用 * * @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()} * @param context */ @Override public void invoke(IndexOrNameData data, AnalysisContext context) { log.info("解析到一条数据:{}", JSON.toJSONString(data)); cachedDataList.add(data); // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM if (cachedDataList.size() >= BATCH_COUNT) { saveData(); // 存储完成清理 list cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); } } /** * 所有数据解析完成了 都会来调用 * * @param context */ @Override public void doAfterAllAnalysed(AnalysisContext context) { // 这里也要保存数据,确保最后遗留的数据也存储到数据库 saveData(); log.info("所有数据解析完成!"); } /** * 加上存储数据库 */ private void saveData() { log.info("{}条数据,开始存储数据库!", cachedDataList.size()); demoDAO.save(cachedDataList); log.info("存储数据库成功!"); } }
分批插入的实现是在invoke方法中,当读取缓存数达到我们预期的插入数量时就进行插入,然后重新更新list,原本的list就会被回收,达到方式内存溢出的效果,可以在这个方法中进行行参数校验,有异常抛出即可
3.编写读入方法 // 写法1: fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 EasyExcel.read(fileName, IndexOrNameData.class, new DemoDataListener()).sheet().doRead(); // 写法2 fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 一个文件一个reader try (ExcelReader excelReader = EasyExcel.read(fileName, IndexOrNameData.class, new DemoDataListener()).build()) { // 构建一个sheet 这里可以指定名字或者no ReadSheet readSheet = EasyExcel.readSheet(0).build(); // 读取一个sheet excelReader.read(readSheet); } 二,动态表头分批导出 1.构建表头和数据 //获取表头 private static List<String> makeHeads() { List<String> heads = new ArrayList<>(); //表头信息 heads.add("唯一标识"); heads.add("名称"); heads.add("类型"); return heads; } //获取数据 private static List<Map<String, Object>> makeData() { List<Map<String, Object>> list = new ArrayList<>(); // Map<String,Object> test1 = new LinkedHashMap<>(); //手动添加测试数据(可根据需要从数据库查询) test1.put("id", 1); test1.put("name", 2); test1.put("str", 3); list.add(test1); // Map<String,Object> test2 = new LinkedHashMap<>(); test2.put("id", 11); test2.put("name", 22); test2.put("str", 33); list.add(test2); return list; } 2.写出代码 public void exportExcel(HttpServletResponse httpServletResponse,@RequestParam(required = false) String fileName,@RequestParam(required = false) List<String> heads, @RequestParam(required = false) List<Map<String, Object>> list) throws IOException { if (StringUtils.isEmpty(fileName)){ //文件名称也可以动态获取 fileName = System.currentTimeMillis() + ".xlsx"; } else { fileName = fileName + ".xlsx"; } if(heads == null || heads.size() == 0){ heads = makeHeads(); } if(list == null || list.size() == 0){ list = makeData(); } OutputStream os= responseInfo(httpServletResponse, fileName); // 调用responseInfo方法 List<List<String>> hs = new ArrayList<>(); for (String s : heads) { hs.add(Arrays.asList(s)); } List<List<Object>> list2 = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { List<Object> objects = new ArrayList<>(); Collection<Object> values = list.get(i).values(); for (Object value : values) { objects.add(value.toString()); } list2.add(objects); } // 头的策略 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 背景设置为红色 headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short)20); headWriteCellStyle.setWriteFont(headWriteFont); // 内容的策略 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); // 背景绿色 contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); WriteFont contentWriteFont = new WriteFont(); // 字体大小 contentWriteFont.setFontHeightInPoints((short)20); contentWriteCellStyle.setWriteFont(contentWriteFont); // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); //创建一个行高设置处理器,我这里直接用匿名内部类类了 AbstractRowHeightStyleStrategy abstractRowHeightStyleStrategy = new AbstractRowHeightStyleStrategy() { @Override protected void setHeadColumnHeight(Row row, int relativeRowIndex) { if (relativeRowIndex==0){ row.setHeightInPoints(50); }else { row.setHeightInPoints(10); } } @Override protected void setContentColumnHeight(Row row, int relativeRowIndex) { //默认主体的高度 row.setHeightInPoints(10); } }; //创建列宽设置处理器 AbstractHeadColumnWidthStyleStrategy abstractHeadColumnWidthStyleStrategy = new AbstractHeadColumnWidthStyleStrategy() { @Override protected Integer columnWidth(Head head, Integer columnIndex) { switch (columnIndex) { case 0: return 6; case 1: return 20; case 2: return 20; default: return 13; } } }; WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); //开始写出 ExcelWriter build = EasyExcel .write(os) .head(hs) //注册内容以及表头处理器 .registerWriteHandler(new HorizontalCellStyleStrategy(headWriteCellStyle ,contentWriteCellStyle)) //注册行高处理器 .registerWriteHandler(abstractRowHeightStyleStrategy) //注册列宽处理器 .registerWriteHandler(abstractHeadColumnWidthStyleStrategy) .build(); //然后就可以用上边的buid对象往指定的sheet中写入数据了,当数据量大的时候,我们就可以 分批写入,伪代码如下 for(a a:list){ build.write(list2,writeSheet); } } /** * 功能:公用方法,写回浏览器 * [response, fileName] * @return {@link OutputStream} * @throws */ public static OutputStream responseInfo(HttpServletResponse response, String fileName) throws IOException { // 这里注意有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); response.setHeader("Content-disposition", "attachment; filename*=utf-8''" + fileName); OutputStream os=response.getOutputStream(); return os; } /** * 如果要兼容swagger用这个,上面的注释掉 * 功能:公用方法 * 参数:fileName 文件名称, 如:123.xlsx public static OutputStream responseInfo(HttpServletResponse response, String fileName) throws IOException { response.setCharacterEncoding("utf-8"); response.setContentType("APPLICATION/OCTET-STREAM"); response.addHeader("Content-Disposition", "attachment;filename=" + fileName); OutputStream os=response.getOutputStream(); return os; } */easyExcel实现分批导入,动态表头分批导出,以及导出表格样式设置由讯客互联游戏开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“easyExcel实现分批导入,动态表头分批导出,以及导出表格样式设置”