如何使用EasyExcel生成多列表组合填充的复杂Excel示例

作者:Funky_oaNiu

一、(需求)生成的表格效果:

其中只有顶部日期、中间表格数据、底部总计数据,是通过模板填充的

表头、绿色区域、列名、背景颜色、四个表格的结构是模板中定义好的

本文主要可学习如何填充数据

二、搞一个模板文件

这模板文件是自己编写的,占位符使用{xx}的形式,注意观察一下表格和外部的站位符区别,表格是{data1.xxx}这种形式,用来标识不同表格

三、建立对应的表格实体类

复制代码
@Data
public class FillData {
    private String name;
    private double number;
    private Date date;
}

四、开始填充

这里是SpringBoot项目,写在Controller里的,仔细看代码注释,这里就不过多描述了

复制代码
@Operation(summary = "导出excel")
@GetMapping("/export-excel")
public void exportExcel(PsbLoc psbLoc, HttpServletResponse response) throws IOException {
    // 读取资源文件(这样打成jar模板文件也在里面,防止模板文件被修改导致功能不可用,文件是在src/main/resources/excelTemplates下的)
    ClassPathResource resource = new ClassPathResource("excelTemplates"+File.separator+"productReceiveSendTemplate.xlsx");
    // 组织并填充模板数据
    ByteArrayOutputStream byteArrayOutputStream = compositeFill(resource.getInputStream());

    // 这里文件名不起作用是因为前端是写死的
    String fileName = "多列表" + System.currentTimeMillis() + ".xlsx";
    // 设置响应头,告诉浏览器这是一个下载的文件,这里文件名
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

    // 将文件内容写入响应输出流,浏览器可以直接触发下载
    response.getOutputStream().write(byteArrayOutputStream.toByteArray());
    response.getOutputStream().flush();
}

private ByteArrayOutputStream compositeFill(InputStream templateInputStream) throws IOException {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    // 使用EasyExcel的模板填充功能,在这里指定合并单元格,这里应该是easyExcel的bug,第一列无法合并,其他列都可以,所以第一列单独用原生poi进行合并
    try (ExcelWriter excelWriter = EasyExcel.write(byteArrayOutputStream).withTemplate(templateInputStream)
            // 这里的参数就是圈了个合并区域,在这个正方形内的单元格都会合并,要注意索引是从0开始的,并且区域内不能存在已经合并的单元格否则报错
            .registerWriteHandler(new OnceAbsoluteMergeStrategy(2, 14, 9, 9))
            .registerWriteHandler(new OnceAbsoluteMergeStrategy(15, 27, 9, 9))
            .build()) {
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        // 防止上面两个表格覆盖下面两个表格,每一行都采用新增一行的方式
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        // 使用模板填充,必须使用FillWrapper,这是官方要求,并且每行两个表格只能有一个表格设置增行,否则会存在一个表格有空行,这里是造的测试数据
        excelWriter.fill(new FillWrapper("data1", makeTestData(1)), fillConfig, writeSheet);
        excelWriter.fill(new FillWrapper("data2", makeTestData(2)), writeSheet);
        excelWriter.fill(new FillWrapper("data3", makeTestData(3)), fillConfig, writeSheet);
        excelWriter.fill(new FillWrapper("data4", makeTestData(4)), writeSheet);

        // 设置表格外的填充数据,例如总计、日期等数据
        HashMap<String, Object> map = new HashMap<>();
        map.put("date", "2024年11月15日");
        map.put("allInStaockQty", 1);
        map.put("allOutStockQty", 2);
        map.put("allConvertStockQty", 3);
        map.put("allStockQty", 4);
        map.put("convertCenterStockQty", 5);
        map.put("mixOutStockQty", 6);
        map.put("endItem1", 7);
        map.put("endItem2", 8);
        map.put("endItem3", 9);
        map.put("endItem4", 10);
        map.put("endItem5", 11);
        excelWriter.fill(map, writeSheet);
    }
    // 合并单元格,由于easyExcel自带的OnceAbsoluteMergeStrategy合并策略bug,这里需要用poi合并一下
    return mergeCells(byteArrayOutputStream);
}

public ByteArrayOutputStream mergeCells(ByteArrayOutputStream inputStream) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputStream.toByteArray());
         Workbook workbook = new XSSFWorkbook(byteArrayInputStream)) {
        // 获取第一个工作表
        Sheet sheet = workbook.getSheetAt(0);

        // 合并第1列第3行到第15行,注意行索引从0开始
        CellRangeAddress range1 = new CellRangeAddress(2, 14, 0,0);
        sheet.addMergedRegion(range1);

        // 合并第1列第16行到第28行,注意行索引从0开始
        CellRangeAddress range2 = new CellRangeAddress(15, 27, 0, 0);
        sheet.addMergedRegion(range2);

        // 设置合并单元格的样式
        CellStyle style = workbook.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        // 将修改后的内容写入 ByteArrayOutputStream
        workbook.write(outputStream);
        // 刷新一下,确保数据完全写入
        outputStream.flush();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return outputStream;
}

private List<FillData> makeTestData(Integer listIndx) {
        List<FillData> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
            FillData fillData = new FillData();
            list.add(fillData);
            fillData.setName("张三"+listIndx);
            fillData.setNumber(5.2);
            fillData.setDate(new Date());
        }
        return list;
    }

五、Vue3前端发起请求下载

复制代码
const handleExport = async () => {
  try {
  	// 这里的确认框、loading框代码就省略了
    // 发起导出
    const data = await ReportTestApi.testReport(queryParams)
    download.excel(data, '测试模板填充.xlsx')
  } catch {
  } finally {
    exportLoading.value = false
  }
}

const download = {
  // 下载 Excel 方法
  excel: (data: Blob, fileName: string) => {
    download0(data, fileName, 'application/vnd.ms-excel')
  },
  // 下载 Word 方法
  word: (data: Blob, fileName: string) => {
    download0(data, fileName, 'application/msword')
  },
  // 下载 Zip 方法
  zip: (data: Blob, fileName: string) => {
    download0(data, fileName, 'application/zip')
  },
  // 下载 Html 方法
  html: (data: Blob, fileName: string) => {
    download0(data, fileName, 'text/html')
  },
  // 下载 Markdown 方法
  markdown: (data: Blob, fileName: string) => {
    download0(data, fileName, 'text/markdown')
  }
}

const download0 = (data: Blob, fileName: string, mineType: string) => {
  // 创建 blob
  const blob = new Blob([data], { type: mineType })
  // 创建 href 超链接,点击进行下载
  window.URL = window.URL || window.webkitURL
  const href = URL.createObjectURL(blob)
  const downA = document.createElement('a')
  downA.href = href
  downA.download = fileName
  downA.click()
  // 销毁超连接
  window.URL.revokeObjectURL(href)
}

六、官方文档及AI问答

直接拉到下面,看"多列表组合填充"
https://easyexcel.opensource.alibaba.com/docs/current/quickstart/fill#模板-2

easyExcel官方问答AI,对比了一下,其他AI都没有它答的好

无疑: https://answer.opensource.alibaba.com/docs/intro

客官点个赞再走呗

相关推荐
考虑考虑6 小时前
Jpa使用union all
java·spring boot·后端
用户3721574261357 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊8 小时前
Java学习第22天 - 云原生与容器化
java
渣哥10 小时前
原来 Java 里线程安全集合有这么多种
java
间彧10 小时前
Spring Boot集成Spring Security完整指南
java
间彧10 小时前
Spring Secutiy基本原理及工作流程
java
Java水解11 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆13 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学14 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole14 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端