导出复杂excel遇到的一些坑
1、填充Excel时填充图片到模板中合并单元格的图片,只占一行一列,并未占满整个合并后的单元格。
2、复杂填充多个list数据数据混乱
解决:
1、EasyExcel图片填充拦截器-修改图片边距-跨行-属性等
主要就是实现了AbstractCellWriteHandler,利用EasyExcel填充的不同时期调用对应方法进行拦截处理:
在数据转换完成后(afterCellDataConverted),将单元格类型设置成EMPTY,不让EasyExcel使用其默认方式处理图片;(AbstractExcelWriteExecutor#setImageValue)
在单元格上的所有操作完成后(afterCellDispose),使用我们自定义的方式对图片处理。
————————————————
版权声明:本文为CSDN博主「IMXF_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_36349646/article/details/118896257
package com.hafnkj.common.config; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.AbstractCellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections4.CollectionUtils; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.util.Units; import java.util.List; import java.util.Objects; /** * EasyExcel图片填充拦截器 * * @author shixf on 2021/7/19 */ @Data @EqualsAndHashCode(callSuper = true) public class ImageCellWriteHandler extends AbstractCellWriteHandler { /** * 图片行列跨度 */ private int colSpan = 1; private int rowSpan = 1; /** * 左侧和右侧边框粗细 */ private int borderPixelX1Y1; private int borderPixelX2Y2; /** * 可以随着单元格一起移动,不改变大小 */ private AnchorType anchorType = AnchorType.MOVE_DONT_RESIZE; public ImageCellWriteHandler() { this.borderPixelX1Y1 = 5; this.borderPixelX2Y2 = 5; } public ImageCellWriteHandler(int borderPixel) { this.borderPixelX1Y1 = borderPixel; this.borderPixelX2Y2 = borderPixel; } public ImageCellWriteHandler(int borderPixelX1Y1, int borderPixelX2Y2) { this.borderPixelX1Y1 = borderPixelX1Y1; this.borderPixelX2Y2 = borderPixelX2Y2; } /** * 单元格数据转换后调用 */ @Override public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 此处不处理表头,不处理不包含图像的 boolean noImageValue = Objects.isNull(cellData) || Objects.isNull(cellData.getImageValue()); if (Objects.equals(Boolean.TRUE, isHead) || noImageValue) { return; } // 设置单元格类型为EMPTY 让EasyExcel不去处理该单元格 由我们自己填充 cellData.setType(CellDataTypeEnum.EMPTY); } /** * 在单元格上的所有操作完成后调用 */ @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List
cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { if (CollectionUtils.isEmpty(cellDataList) || Objects.equals(Boolean.TRUE, isHead)) { return; } // cellDataList 是list的原因是 填充的情况下 可能会多个写到一个单元格 但是如果普通写入只有一个 CellData cellData = cellDataList.get(0); // 在afterCellDataConverted方法里面已经将CellDataType设置为EMPTY 此处不能用cellData.getType()来判断是否图片类型 if (Objects.isNull(cellData) || Objects.isNull(cellData.getImageValue())) { return; } setImageValue(cellData, cell); } private void setImageValue(CellData cellData, Cell cell) { Sheet sheet = cell.getSheet(); int index = sheet.getWorkbook().addPicture(cellData.getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); Drawing drawing = sheet.getDrawingPatriarch(); if (drawing == null) { drawing = sheet.createDrawingPatriarch(); } CreationHelper helper = sheet.getWorkbook().getCreationHelper(); ClientAnchor anchor = helper.createClientAnchor(); // 图片边距:让图片不会填满整个单元格,与四周有一定边距 final int borderWidth1 = Units.pixelToEMU(getBorderPixelX1Y1()); final int borderWidth2 = Units.pixelToEMU(getBorderPixelX2Y2()); // 图片左上角偏移量 anchor.setDx1(0); anchor.setDy1(0); // 图片右下角偏移量 anchor.setDx2(0); anchor.setDy2(0); // 图片行列 anchor.setCol1(6); anchor.setCol2(8); anchor.setRow1(2); anchor.setRow2(5); anchor.setAnchorType(getAnchorType()); drawing.createPicture(anchor, index); // 按照图片本身实际尺寸 //Picture picture = drawing.createPicture(anchor, index); //picture.resize(); } }
主要修改以下代码
// 图片左上角偏移量
anchor.setDx1(0);
anchor.setDy1(0);
// 图片右下角偏移量
anchor.setDx2(0);
anchor.setDy2(0);
// 图片行列
anchor.setCol1(6);
anchor.setCol2(8);
anchor.setRow1(2);
anchor.setRow2(5);
anchor.setAnchorType(getAnchorType());
drawing.createPicture(anchor, index);
// 按照图片本身实际尺寸
//Picture picture = drawing.createPicture(anchor, index);
//picture.resize();
注册Handler
ImageCellWriteHandler imageCellWriteHandler = new ImageCellWriteHandler();
// imageCellWriteHandler.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
...
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
.registerWriteHandler(imageCellWriteHandler )
.build();
7
这样就可以改变图片位置,目前只适合一张图片
2、如果有多个list 模板上必须有{前缀.} 这里的前缀就是 data1,然后多个list必须用 FillWrapper包裹
官网代码
/**
* 多列表组合填充填充
*
* @since 2.2.0-beta1
*/
@Test
public void compositeFill() {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// {} 代表普通变量 {.} 代表是list的变量 {前缀.} 前缀可以区分不同的list
String templateFileName =
TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "composite.xlsx";
String fileName = TestFileUtil.getPath() + "compositeFill" + System.currentTimeMillis() + ".xlsx";
// 方案1
try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet().build();
FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
// 如果有多个list 模板上必须有{前缀.} 这里的前缀就是 data1,然后多个list必须用 FillWrapper包裹
excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet);
excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet);
excelWriter.fill(new FillWrapper("data2", data()), writeSheet);
excelWriter.fill(new FillWrapper("data2", data()), writeSheet);
excelWriter.fill(new FillWrapper("data3", data()), writeSheet);
excelWriter.fill(new FillWrapper("data3", data()), writeSheet);
Map map = new HashMap();
//map.put("date", "2019年10月9日13:28:28");
map.put("date", new Date());
excelWriter.fill(map, writeSheet);
}
}