# easyexcelExport **Repository Path**: NullPointerli/easyexcel-export ## Basic Information - **Project Name**: easyexcelExport - **Description**: easyexcel根据模板导出excel,并生成二维码,并将生成的二维码图片进行放大 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-04-12 - **Last Updated**: 2024-04-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 使用的前提 #### jdk 1.8 #### spring-boot 2.6.13 #### easyexcel 2.2.10 (easyexcel 3.x 略有不同) #### 生成二维码需要的两个依赖 ##### com.google.zxing 3.4.1 ##### hutool 5.2.2 ### 最终效果 #### 模板 ![img](assets/1712884735896-647dc445-bb93-4a0c-9bb9-4a773d6005ab.webp) #### 最终导出效果(二维码做了一些拉伸) ![image.png](assets/1712884793958-fe634ad5-fb80-4227-86f3-4c14ecb43b75.webp) ### 引入依赖 ```xml org.springframework.boot spring-boot-starter-web org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test com.alibaba easyexcel 2.2.10 com.google.zxing core 3.4.1 cn.hutool hutool-all 5.2.2 ``` ### 实际的实现方式 ##### 在写模板的时候,如果是从list中获取的话,就用{.xxxxx}(注意有个 .),如果是之填充一个的话就没有这个“.”了用{xxxxx}表示; 在代码中写的话要用一下这种方式 ```java // 准备数据 // 定义需要导出的 实体list List easyexcelExportModels = new ArrayList<>(); for (int i = 0; i < 10; i++) { EasyexcelExportModel model = new EasyexcelExportModel(); model.setId(i); model.setName("name" + i); model.setAge(i*10); // 添加二维码,把二维码转为byte[] byte[] qrCode = QrCodeUtil.generatePng(i+"1", QrConfig.create().setCharset(Charset.forName("utf-8"))); model.setQrCode(qrCode); easyexcelExportModels.add(model); } // 添加需要导出的特殊字段值 Map map = new HashMap<>(); map.put("operator","操作员007"); map.put("Signer","签收员009"); map.put("bigQrCode",QrCodeUtil.generatePng("bigQrCode", QrConfig.create().setCharset(Charset.forName("utf-8")))); // 模板的输入流 InputStream projectPath = this.getClass().getClassLoader().getResourceAsStream("excelTemplate"+ File.separator+ "exportExcelByTemplate.xlsx"); // 通过输出流的方式将写成的excel返回给前端 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); //防止中文乱码 response.setHeader("Content-disposition", "attachment;filename=" + System.currentTimeMillis() + ".xlsx"); ExcelWriter excelWriter = EasyExcel // 写入到输出流当中 .write(response.getOutputStream()) // 携带的模板信息 .withTemplate(projectPath).build(); // 将特殊字段的值赋值 excelWriter.fill(map,writeSheet); excelWriter.fill(easyexcelExportModels,writeSheet); ``` ##### 如果模板中最后一行有特殊含义 例如:最后一行是签收员
![image.png](assets/1712886895423-0a4f60a3-f26c-4c33-a8e4-ca8a4208bb63.webp)
这时候在 创建excel的时候就需要加一个配置了 ```java // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 //forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 // 如果数据量大 list不是最后一行 参照下一个 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); excelWriter.fill(easyexcelExportModels,fillConfig,writeSheet); ``` 下面这个是easyexcel的官方文档 也有提到
[填充Excel | Easy Excel](https://easyexcel.opensource.alibaba.com/docs/current/quickstart/fill#since-1)
![image.png](assets/1712887134515-22a5ba5e-2184-4c29-a6b6-514195693654.webp) ##### 重点,本项目当中需要将生成的二维码进行放大 `**这里大致的意思就是,**`
`**1.在数据转化的时候,自己需要进行拦截一下,不让easyexcel自动生成;**`
`**2.在最后都转化完成之后,自己在生成一个图片放在画布上**`
这里就需要重写一个方法了,即new CellWriteHandler() ```java ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) .registerWriteHandler( new CellWriteHandler() { // 在创建单元格之前调用 @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } // 创建单元格后调用 @Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { } // 单元格数据转换后调用 @Override public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 因为导出的 二维码图片都在第 4列,这里先将图片列设置为空; // 因为如果这里不设置为空的话,后面再根据这个拉伸的话,这小二维码还会存在 int columnIndex = cell.getColumnIndex(); byte[] imageValue = cellData.getImageValue(); if ((3==columnIndex || 7 == columnIndex) && imageValue != null) { cellData.setType(CellDataTypeEnum.EMPTY); } } // 在单元上的所有操作完成后调用 @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 设置样式 // 获取当前sheet Sheet sheet = cell.getSheet(); int columnIndex = cell.getColumnIndex(); if (columnIndex == 3) { // 这里一定要有,这就是假设在画布上画上 上面设置为空的二维码,并返回其索引;在后面真正的拉伸之后的图片还是放在这个索引上 int index = sheet.getWorkbook().addPicture(cellDataList.get(0).getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); Drawing drawing = sheet.getDrawingPatriarch(); // 如果画布为空就创建一个 if (drawing == null) { drawing = sheet.createDrawingPatriarch(); } CreationHelper helper = sheet.getWorkbook().getCreationHelper(); ClientAnchor anchor = helper.createClientAnchor(); // 设置图片坐标 anchor.setDx1(0); anchor.setDx2(0); anchor.setDy1(0); anchor.setDy2(0); //设置图片位置 anchor.setCol1(cell.getColumnIndex()); anchor.setCol2(cell.getColumnIndex() + 2); anchor.setRow1(cell.getRowIndex()); anchor.setRow2(cell.getRowIndex()+1); // 设置图片可以随着单元格移动 anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); // drawing.createPicture(anchor, 0); drawing.createPicture(anchor,index); } else if (columnIndex == 7) {// 处理大二维码 byte[] imageValue = cellDataList.get(0).getImageValue(); // 需要先判断大二维码值存在的时候才进行转化 if (imageValue != null) { // 这里一定要有,这就是假设在画布上画上 上面设置为空的二维码,并返回其索引;在后面真正的拉伸之后的图片还是放在这个索引上 int index = sheet.getWorkbook().addPicture(cellDataList.get(0).getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); Drawing drawing = sheet.getDrawingPatriarch(); // 如果画布为空就创建一个 if (drawing == null) { drawing = sheet.createDrawingPatriarch(); } CreationHelper helper = sheet.getWorkbook().getCreationHelper(); ClientAnchor anchor = helper.createClientAnchor(); // 设置图片坐标 anchor.setDx1(0); anchor.setDx2(0); anchor.setDy1(0); anchor.setDy2(0); //设置图片位置 anchor.setCol1(cell.getColumnIndex()); anchor.setCol2(cell.getColumnIndex() + 2); anchor.setRow1(cell.getRowIndex()); anchor.setRow2(cell.getRowIndex() + 3); // 设置图片可以随着单元格移动 anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); // drawing.createPicture(anchor, 0); drawing.createPicture(anchor, index); } } } }) .withTemplate(projectPath).build(); ``` ### 完整代码 ```java package com.easyexcelexport.server.Impl; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.extra.qrcode.QrCodeUtil; import cn.hutool.extra.qrcode.QrConfig; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.fill.FillConfig; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.easyexcelexport.entity.EasyexcelExportModel; import com.easyexcelexport.server.EasyexcelExportService; import lombok.extern.slf4j.Slf4j; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.*; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Service @Slf4j public class EasyexcelExportServiceImpl implements EasyexcelExportService { /** * 根据模板导出excel */ @Override public void exportExcelByTemplate(HttpServletResponse response) throws IOException { // 定义需要导出的 实体list List easyexcelExportModels = new ArrayList<>(); for (int i = 0; i < 10; i++) { EasyexcelExportModel model = new EasyexcelExportModel(); model.setId(i); model.setName("name" + i); model.setAge(i*10); // 添加二维码,把二维码转为byte[] byte[] qrCode = QrCodeUtil.generatePng(i+"1", QrConfig.create().setCharset(Charset.forName("utf-8"))); model.setQrCode(qrCode); easyexcelExportModels.add(model); } // 添加需要导出的特殊字段值 Map map = new HashMap<>(); map.put("operator","操作员007"); map.put("Signer","签收员009"); map.put("bigQrCode",QrCodeUtil.generatePng("bigQrCode", QrConfig.create().setCharset(Charset.forName("utf-8")))); log.info("=====easyexcel处理开始=========="); InputStream projectPath = this.getClass().getClassLoader().getResourceAsStream("excelTemplate"+ File.separator+ "exportExcelByTemplate.xlsx"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); //防止中文乱码 response.setHeader("Content-disposition", "attachment;filename=" + System.currentTimeMillis() + ".xlsx"); // 这里 需要指定写用哪个class去写 ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) .registerWriteHandler( new CellWriteHandler() { // 在创建单元格之前调用 @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } // 创建单元格后调用 @Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { } // 单元格数据转换后调用 @Override public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 因为导出的 二维码图片都在第 4列,这里先将图片列设置为空;因为如果这里不设置为空的话, // 后面再根据这个拉伸的话,这小二维码还会存在 int columnIndex = cell.getColumnIndex(); byte[] imageValue = cellData.getImageValue(); if ((3==columnIndex || 7 == columnIndex) && imageValue != null) { cellData.setType(CellDataTypeEnum.EMPTY); } } // 在单元上的所有操作完成后调用 @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 设置样式 // 获取当前sheet Sheet sheet = cell.getSheet(); int columnIndex = cell.getColumnIndex(); if (columnIndex == 3) { // 这里一定要有,这就是假设在画布上画上 上面设置为空的二维码,并返回其索引;在后面真正的拉伸之后的图片还是放在这个索引上 int index = sheet.getWorkbook().addPicture(cellDataList.get(0).getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); Drawing drawing = sheet.getDrawingPatriarch(); // 如果画布为空就创建一个 if (drawing == null) { drawing = sheet.createDrawingPatriarch(); } CreationHelper helper = sheet.getWorkbook().getCreationHelper(); ClientAnchor anchor = helper.createClientAnchor(); // 设置图片坐标 anchor.setDx1(0); anchor.setDx2(0); anchor.setDy1(0); anchor.setDy2(0); //设置图片位置 anchor.setCol1(cell.getColumnIndex()); anchor.setCol2(cell.getColumnIndex() + 2); anchor.setRow1(cell.getRowIndex()); anchor.setRow2(cell.getRowIndex()+1); // 设置图片可以随着单元格移动 anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); // drawing.createPicture(anchor, 0); drawing.createPicture(anchor,index); } else if (columnIndex == 7) {// 处理大二维码 byte[] imageValue = cellDataList.get(0).getImageValue(); // 需要先判断大二维码值存在的时候才进行转化 if (imageValue != null) { // 这里一定要有,这就是假设在画布上画上 上面设置为空的二维码,并返回其索引;在后面真正的拉伸之后的图片还是放在这个索引上 int index = sheet.getWorkbook().addPicture(cellDataList.get(0).getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); Drawing drawing = sheet.getDrawingPatriarch(); // 如果画布为空就创建一个 if (drawing == null) { drawing = sheet.createDrawingPatriarch(); } CreationHelper helper = sheet.getWorkbook().getCreationHelper(); ClientAnchor anchor = helper.createClientAnchor(); // 设置图片坐标 anchor.setDx1(0); anchor.setDx2(0); anchor.setDy1(0); anchor.setDy2(0); //设置图片位置 anchor.setCol1(cell.getColumnIndex()); anchor.setCol2(cell.getColumnIndex() + 2); anchor.setRow1(cell.getRowIndex()); anchor.setRow2(cell.getRowIndex() + 3); // 设置图片可以随着单元格移动 anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); // drawing.createPicture(anchor, 0); drawing.createPicture(anchor, index); } } } }) .withTemplate(projectPath).build(); // 这里注意 如果同一个sheet只要创建一次 WriteSheet writeSheet = EasyExcel.writerSheet().build(); if (CollectionUtil.isNotEmpty(map)){ // 将特殊字段的值赋值 excelWriter.fill(map,writeSheet); } // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 //forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 // 如果数据量大 list不是最后一行 参照下一个 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); excelWriter.fill(easyexcelExportModels,fillConfig,writeSheet); log.info("=====easyexcel处理结束========="); // 关闭流 projectPath.close(); if (excelWriter != null) { excelWriter.finish(); } } } ```