easyExcel实现分批导入,动态表头分批导出,以及导出表格样式设置

        
            com.alibaba
            easyexcel
            2.2.6
        

一,分批导入

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 {

    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List 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 makeHeads() {
        List heads = new ArrayList<>(); //表头信息
        heads.add("唯一标识");
        heads.add("名称");
        heads.add("类型");
        return heads;
    }

//获取数据
    private static List> makeData() {
        List> list = new ArrayList<>();
        //
        Map test1 = new LinkedHashMap<>(); //手动添加测试数据(可根据需要从数据库查询)
        test1.put("id", 1);
        test1.put("name", 2);
        test1.put("str", 3);
        list.add(test1);
        //
        Map 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 heads, @RequestParam(required = false) List> 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> hs = new ArrayList<>();
        for (String s : heads) {
            hs.add(Arrays.asList(s));
        }
        List> list2 = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            List objects = new ArrayList<>();
            Collection 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;
     }
     */




       
        
        

你可能感兴趣的:(java,中间件,java,开发语言)