✅EasyExcel数据导出(根据模板和导出图片(二维码))
java整合easyexcell导出 ,并将生成的二维码进行拉伸操作
·
使用的前提
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
最终效果
模板
最终导出效果(二维码做了一些拉伸)
引入依赖
<!-- web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- spring-boot-starter-test依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- easyexcel依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.10</version>
</dependency>
<!--二维码需要使用到的-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<!-- hutool工具类-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.2.2</version>
</dependency>
实际的实现方式
在写模板的时候,如果是从list中获取的话,就用{.xxxxx}(注意有个 .),如果是之填充一个的话就没有这个“.”了用{xxxxx}表示;
在代码中写的话要用一下这种方式
// 准备数据
// 定义需要导出的 实体list
List<EasyexcelExportModel> 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<String,Object> 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);
如果模板中最后一行有特殊含义
例如:最后一行是签收员
这时候在 创建excel的时候就需要加一个配置了
// 这里注意 入参用了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
重点,本项目当中需要将生成的二维码进行放大
**这里大致的意思就是,**
**1.在数据转化的时候,自己需要进行拦截一下,不让easyexcel自动生成;**
**2.在最后都转化完成之后,自己在生成一个图片放在画布上**
这里就需要重写一个方法了,即new CellWriteHandler()
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<CellData> 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();
完整代码
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<EasyexcelExportModel> 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<String,Object> 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<CellData> 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();
}
}
}

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)