文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java使用poi导出excel针对不同数据列配置设置不同单元格格式(适用于通用导出excel数据)

2023-09-08 13:43

关注

Java使用poi导出excel针对不同数据配置设置不同单元格格式

公司大部分业务都是查询相关的业务, 所以建了一个项目专门做数据查询, 数据中转等抽象通用的业务, 有一天给我安排了一个功能, 做excel导出, 配置好查询sql和表头字段映射后即可导出excel, 无需修改代码
后来因为导出数据要求保留几位小数或者转换成百分比等设置单元格格式需要支持配置化, 由于做数据中转有些系统需要的数据是不需要约束小数的, 所以数据层面不能做约束, 否则就不是真正意义上的通用了

项目中使用了hutool工具类库, 封装了很多的方法, 大大的降低了开发成本
第一版代码, 想着hutool工具里面有个ExcelUtil的类, 里面的方法对使用poi导出excel进行了封装简化于是就这么写了

实现方案

此处以商品订单为例, 不考虑一笔订单多个商品的情况, 不做单元格合并操作
这里使用的Hutool版本是5.8.15

<dependency>      <groupId>cn.hutoolgroupId>      <artifactId>hutool-allartifactId>      <version>5.8.15version>dependency>

BaseFileUtils.java

public abstract class BaseFileUtils {        public static void writeXlsxByData(String fileName, List<Object> headList, List<List<Object>> dataList, HttpServletResponse response) {        //创建xlsx格式        ExcelWriter writer = ExcelUtil.getBigWriter();        for (int i = 0; i < dataList.size(); i++) {            writer.setRowHeight(i, 30);        }        for (int i = 0; i < headList.size(); i++) {            writer.setColumnWidth(i, 30);        }        writer.writeHeadRow(headList);        // 一次性写出内容,使用默认样式,强制输出        writer.write(dataList, false);        writeXlsx2Response(response, writer, fileName);    }        private static void writeXlsx2Response(HttpServletResponse response, ExcelWriter writer, String fileName) {        //out为OutputStream,需要写出到的目标流        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");        response.setHeader("Content-Disposition","attachment;filename=" + fileName);        try {            writer.flush(response.getOutputStream(), true);        } catch (IOException e) {            e.printStackTrace();        }        // 关闭writer,释放内存        writer.close();    }}

FileService.java

@Servicepublic class FileServiceImpl implements IFileService {    @Override    public void exportExcel(HttpServletResponse response) {        //  列头信息        List<Object> headList = new ArrayList<>(Arrays.asList("订单号", "商品名称", "单价", "数量", "总价", "占收入百分比"));        //  数据集        List<Object> data1 = new ArrayList<>();        data1.add("3123ab3412c3");        data1.add("鼠标");        data1.add(new BigDecimal("89.9"));        data1.add(1);        data1.add(new BigDecimal("89.9"));        data1.add(new BigDecimal("0.1"));        List<Object> data2 = new ArrayList<>();        data2.add("3123ab3412c4");        data2.add("键盘");        data2.add(new BigDecimal("298.9"));        data2.add(2);        data2.add(new BigDecimal("597.8"));        data2.add(new BigDecimal("0.321300"));        List<Object> data3 = new ArrayList<>();        data3.add("3123ab3412c5");        data3.add("显卡");        data3.add(new BigDecimal("2048.6"));        data3.add(1);        data3.add(new BigDecimal("2048.6"));        data3.add(new BigDecimal("0.5423"));        List<Object> data4 = new ArrayList<>();        data4.add("3123ab3412c6");        data4.add("显示器");        data4.add(new BigDecimal("1999.9"));        data4.add(1);        data4.add(new BigDecimal("1999.9"));        data4.add(new BigDecimal("0.36740"));        List<List<Object>> dataList = new ArrayList<>(Arrays.asList(data1, data2, data3, data4));        BaseFileUtils.writeXlsxByData("订单信息", headList, dataList, response);    }

FileController.java

@RestController@RequestMapping("/file")public class FileController {    @Resource    private IFileService fileService;    @GetMapping("/excel")    public void exportExcel(HttpServletResponse response) {        fileService.exportExcel(response);    }}

导出结果
在这里插入图片描述到此第一版的实现已经完成了

第二版需要支持单元格格式配置话, 这里的配置是在列头名称的配置那里一起配置写入
原本的列头就只是一个普通字符串"订单号", "商品名称"`, "单价", ...
现在需要额外配置格式的需要改成json字符串, 自定义一些规则, 例如:

{"text": "总价","excel_format": {number_format: "0.0"}}{"text": "商品名称","excel_format": {ground_color: "red"}}{"text": "占收入百分比","excel_format": {number_format: "0.00%"}}

理想中的方案

当时因为是用了Hutool的工具包做的, 于是就想着看看这个工具里面是不是有封装一些修改单元格格式的工具方法, 于是找到了以下方法
这个是ExcelWriter对象的方法, 该方法可以获取对应的一些样式集
通过一下四个方法
根据需要的格式调用上述四个方法
所以基于第一版进行修改如下:
修改writeXlsxByData方法

    public static void writeXlsxByData(String fileName, List<Object> headList, List<List<Object>> dataList, HttpServletResponse response) {        //创建xlsx格式        ExcelWriter writer = ExcelUtil.getBigWriter();        for (int i = 0; i < dataList.size(); i++) {            writer.setRowHeight(i, 30);        }        List<Object> newHeadList = new ArrayList<>();        for (int i = 0; i < headList.size(); i++) {            writer.setColumnWidth(i, 30);            //  由于不知道这个列头的信息是普通字符串的, 还是JSON字符串, 所以这里通过强转捕获的形式来区分, 当然也可以使用字符串判断, 具体可以根据列头规则定义            try {                JSONObject jsonObject = JSONObject.parseObject((String) headList.get(i));                String text = jsonObject.getString("text");                newHeadList.add(text);                JSONObject excelFormat = jsonObject.getJSONObject("excel_format");                if (excelFormat != null) {                    String numberFormat = excelFormat.getString("number_format");                    String ratioFormat = excelFormat.getString("ratio_format");                    StyleSet styleSet = writer.getStyleSet();                    CellStyle cellStyle = null;                    //  数字格式化                    if (StringUtils.isNotBlank(numberFormat)) {                        cellStyle = styleSet.getCellStyleForNumber();                        cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat(numberFormat));                    }                    //  百分比格式化                    if (StringUtils.isNotBlank(ratioFormat)) {                        cellStyle = styleSet.getCellStyleForNumber();                        cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat(ratioFormat));                    }                    writer.setColumnStyleIfHasData(i, 1, cellStyle);                }            } catch (JSONException ignored) {                newHeadList.add(headList.get(i));            }        }        writer.writeHeadRow(newHeadList);        // 一次性写出内容,使用默认样式,强制输出        writer.write(dataList, false);        writeXlsx2Response(response, writer, fileName);    }

将刚刚写在service上的测试数据也改成配置格式

public void exportExcel(HttpServletResponse response) {        //  列头信息        List<Object> headList = new ArrayList<>(Arrays.asList(                "订单号",                """                {                "text": "商品名称",                "excel_format": {                ground_color: "red"                }                }                """,                "单价",                "数量",                """                {                "text": "总价",                "excel_format": {                number_format: "0.0"                }                }                """,                """                {                "text": "占收入百分比",                "excel_format": {                "ratio_format": "0.00%"                }                }                """));        //  数据集        List<Object> data1 = new ArrayList<>();        data1.add("3123ab3412c3");        data1.add("鼠标");        data1.add(new BigDecimal("89.9"));        data1.add(1);        data1.add(new BigDecimal("89.9"));        data1.add(new BigDecimal("0.1"));        List<Object> data2 = new ArrayList<>();        data2.add("3123ab3412c4");        data2.add("键盘");        data2.add(new BigDecimal("298.9"));        data2.add(2);        data2.add(new BigDecimal("597.8"));        data2.add(new BigDecimal("0.321300"));        List<Object> data3 = new ArrayList<>();        data3.add("3123ab3412c5");        data3.add("显卡");        data3.add(new BigDecimal("2048.6"));        data3.add(1);        data3.add(new BigDecimal("2048.6"));        data3.add(new BigDecimal("0.5423"));        List<Object> data4 = new ArrayList<>();        data4.add("3123ab3412c6");        data4.add("显示器");        data4.add(new BigDecimal("1999.9"));        data4.add(1);        data4.add(new BigDecimal("1999.9"));        data4.add(new BigDecimal("0.36740"));        List<List<Object>> dataList = new ArrayList<>(Arrays.asList(data1, data2, data3, data4));        BaseFileUtils.writeXlsxByData("订单信息", headList, dataList, response);    }

最后导出结果如下
在这里插入图片描述看到结果发现虽然格式设置是没问题了, 但是只有最后一个设置的格式生效, 并且所有设置单元格的列都生效了百分比的格式, 很明显这不是理想中的结果
后面试过了好多方案都以失败告终了, 可能是这个每一次设置的样式都是全局生效的, 所以此时决定放弃使用Hutool封装的方法了, 还是老老实实的用原生POI做了

可实行的方案

导入poi依赖

<dependency>     <groupId>org.apache.poigroupId>     <artifactId>poi-ooxmlartifactId>     <version>5.2.2version>dependency>

在BaseFileUtils.java中新写方法

        public static void writeXlsxForPoi(String fileName, List<Object> headList, List<List<Object>> dataList, HttpServletResponse response) {        Workbook wb = WorkbookUtil.createBook(true);        Sheet sheet = wb.createSheet();        //  设置列宽默认为25        sheet.setDefaultColumnWidth(25);        //  创建第一行表头        Row headRow = sheet.createRow(0);        //  创建的单元格格式        CellStyle headCellStyle = wb.createCellStyle();        CellStyle dataCellStyle = wb.createCellStyle();        //  构建列头格式        buildHeadCellStyle(headCellStyle);        //  构建数据单元格格式        buildDataCellStyle(dataCellStyle);        //  设置第一行--列名        setHeadLine(headList, headRow, headCellStyle);        //  填充数据        setDataInfo(dataList, headList, sheet, dataCellStyle, wb);        //  写出excel        buildExcelDocument(fileName, wb, response);    }        private static void setDataInfo(List<List<Object>> dataList, List<Object> headList, Sheet sheet, CellStyle dataCellStyle, Workbook wb) {        Cell cell;        for (int i = 0; i < dataList.size(); i++) {            Row row = sheet.createRow(i + 1);            List<Object> data = dataList.get(i);            for (int j = 0; j < data.size(); j++) {                cell = row.createCell(j);                Object value = data.get(j);                if (value != null) {                    if (value instanceof Date) {                        cell.setCellValue(value.toString());                    }else if (value instanceof BigDecimal) {                        cell.setCellValue(((BigDecimal) value).doubleValue());                    } else if (value instanceof Double) {                        cell.setCellValue(Double.parseDouble(value.toString()));                    }else {                        cell.setCellValue(value.toString());                    }                }                //  新建一个cellStyle, 将需要的样式信息复制过来, 用同一个样式对象最后会只生效最后一个                CellStyle cellStyle = wb.createCellStyle();                cellStyle.cloneStyleFrom(dataCellStyle);                try {                    JSONObject jsonObject = JSONObject.parseObject((String) headList.get(j));                    JSONObject excelFormat = jsonObject.getJSONObject("excel_format");                    if (excelFormat != null) {                        String numberFormat = excelFormat.getString("number_format");                        String ratioFormat = excelFormat.getString("ratio_format");                        String groundColor = excelFormat.getString("ground_color");                        if (StringUtils.isNotBlank(numberFormat)) {cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat(numberFormat));                        }                        if (StringUtils.isNotBlank(ratioFormat)) {cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat(ratioFormat));                        }                        if (StringUtils.isNotBlank(groundColor)) {//  设置单元格颜色cellStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.RED.getIndex());//  设置填充样式(实心填充),不设置填充样式不会有颜色cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);                        }                    }                } catch (JSONException ignored) {                }                cell.setCellStyle(cellStyle);            }        }    }        private static void setHeadLine(List<Object> headList, Row row, CellStyle headCellStyle) {        Cell cell;        for (int i = 0; i < headList.size(); i++) {            cell = row.createCell(i);            Object obj = headList.get(i);            try {                JSONObject jsonObject = JSONObject.parseObject((String) obj);                cell.setCellValue(jsonObject.getString("text"));                cell.setCellStyle(headCellStyle);            } catch (JSONException e) {                cell.setCellValue(headList.get(i).toString());                cell.setCellStyle(headCellStyle);            }        }    }        private static void buildHeadCellStyle(CellStyle cellStyle) {        //  设置单元格居中        cellStyle.setAlignment(HorizontalAlignment.CENTER);        //  设置单元格颜色        cellStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.GREY_25_PERCENT.getIndex());        //  设置填充样式(实心填充),不设置填充样式不会有颜色        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);        //设置边框        cellStyle.setBorderBottom(BorderStyle.THIN);        cellStyle.setBorderLeft(BorderStyle.THIN);        cellStyle.setBorderRight(BorderStyle.THIN);        cellStyle.setBorderTop(BorderStyle.THIN);    }        private static void buildDataCellStyle(CellStyle dataCellStyle) {        //  设置单元格居中        dataCellStyle.setAlignment(HorizontalAlignment.CENTER);        //设置边框        dataCellStyle.setBorderBottom(BorderStyle.THIN);        dataCellStyle.setBorderLeft(BorderStyle.THIN);        dataCellStyle.setBorderRight(BorderStyle.THIN);        dataCellStyle.setBorderTop(BorderStyle.THIN);    }        private static void buildExcelDocument(String fileName, Workbook wb, HttpServletResponse response){        try {            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");            response.setHeader("Content-Disposition", "attachment;filename="+ fileName);            OutputStream outputStream = response.getOutputStream();            wb.write(outputStream);            outputStream.flush();            outputStream.close();            wb.close();        } catch (IOException e) {            logger.error(e.getMessage(),e);        }    }

最后将FileService.java中调用writeXlsxByData()方法改为调用writeXlsxForPoi()方法即可;
导出结果
在这里插入图片描述正是理想中的结果

主要的关键点在于每一次设置样式都要创建一个新的样式对象, 否则仍会出现第一个方案中全局生效最后一个设置的样式一样的结果, 若是有需要默认的全局样式可以通过cellStyle.cloneStyleFrom()方法将原有的格式进行克隆保留原有样式, 使用第一种方案的时候或许也可以以此改良, 有兴趣的可以试试!

本次记录到此结束, 总的来说也算是有一点点小小的进步吧, 可能有不对的地方或者是有其他更好的方案希望各位大佬多多指点!

来源地址:https://blog.csdn.net/weixin_45146530/article/details/130491601

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯