在使用 EasyExcel 導出 Excel 時,如果某個單元格是圖片內容,且存在多張圖片,此時就需要單元格根據圖片數量動態設置寬度。
經過自己的研究和實驗,導出效果如下:
具體代碼如下:
- EasyExcel 版本
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.2</version>
</dependency>
- 定義Excel圖片鏈接轉換工具
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ImageData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.IoUtils;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;/*** Excel圖片鏈接轉換工具** @author 天航星* @date 2024-07-05 15:03*/
public class ExcelImageUrlConverterUtils implements Converter<List<String>> {@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic WriteCellData<?> convertToExcelData(List<String> value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {List<ImageData> imageDataList = new ArrayList<>();for (String url : value) {try {URL imageUrl = new URL(url);byte[] bytes = IoUtils.toByteArray(imageUrl.openConnection().getInputStream());ImageData imageData = new ImageData();imageData.setImage(bytes);imageDataList.add(imageData);} catch (Exception e) {e.printStackTrace();}}WriteCellData writeCellData = new WriteCellData();writeCellData.setImageDataList(imageDataList);writeCellData.setType(CellDataTypeEnum.STRING);return writeCellData;}
}
- 定義圖片屬性
@ExcelProperty(value = "現場圖片(模擬機器人視角)", converter = ExcelImageUrlConverterUtils.class)
private List<String> images;
- 定義單元格圖片寫入攔截器
import cn.hutool.core.util.ObjUtil;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.ImageData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
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.CreationHelper;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.util.Units;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;/*** 單元格圖片寫入攔截器* * @author 天航星* @date 2024-07-05 15:03*/
public class ImageCellWriteHandler implements CellWriteHandler {private final Map<String,List<ImageData>> imageDataMap = new HashMap<>();/*** 單元格的圖片最大張數(每列的單元格圖片張數不確定,單元格寬度需按照張數最多的長度來設置)*/private final AtomicReference<Integer> MAX_IMAGE_SIZE = new AtomicReference<>(0);@Overridepublic void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, WriteCellData<?> cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {if (isHead) {return;}// 將單元格圖片數據復制出來,清空單元格圖片數據if (!ObjUtil.isEmpty(cellData.getImageDataList())) {imageDataMap.put(cell.getRowIndex() + "_" + cell.getColumnIndex(), cellData.getImageDataList());cellData.setType(CellDataTypeEnum.EMPTY);cellData.setImageDataList(new ArrayList<>());}}@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {if (isHead || ObjUtil.isEmpty(cellDataList)) {return;}String key = cell.getRowIndex() + "_" + cell.getColumnIndex();List<ImageData> imageDataList = imageDataMap.get(key);if (ObjUtil.isEmpty(imageDataList)) {return;}if (imageDataList.size() > MAX_IMAGE_SIZE.get()) {MAX_IMAGE_SIZE.set(imageDataList.size());}Sheet sheet = cell.getSheet();// 設置單元格行高sheet.getRow(cell.getRowIndex()).setHeight((short) 900);// 設置單元格列寬(乘多少代表容納多少張圖片)sheet.setColumnWidth(cell.getColumnIndex(), MAX_IMAGE_SIZE.get() > 0 ? 3493 * MAX_IMAGE_SIZE.get() : 3493);// 插入圖片for (int i = 0; i < imageDataList.size(); i++) {ImageData imageData = imageDataList.get(i);if (ObjUtil.isEmpty(imageData)) {continue;}byte[] image = imageData.getImage();this.insertImage(sheet, cell, image, i);}imageDataMap.remove(key);}private void insertImage(Sheet sheet, Cell cell, byte[] pictureData, int i) {// 圖片寬度int pictureWidth = Units.pixelToEMU(100);int index = sheet.getWorkbook().addPicture(pictureData, HSSFWorkbook.PICTURE_TYPE_PNG);Drawing<?> drawing = sheet.getDrawingPatriarch();if (ObjUtil.isEmpty(drawing)) {drawing = sheet.createDrawingPatriarch();}CreationHelper helper = sheet.getWorkbook().getCreationHelper();ClientAnchor anchor = helper.createClientAnchor();// 設置圖片在哪個單元格中anchor.setCol1(cell.getColumnIndex());anchor.setCol2(cell.getColumnIndex());anchor.setRow1(cell.getRowIndex());anchor.setRow2(cell.getRowIndex() + 1);// 設置圖片在單元格中的位置anchor.setDx1(pictureWidth * i);anchor.setDx2(pictureWidth + pictureWidth * i);anchor.setDy1(0);anchor.setDy2(0);// 設置圖片可以隨著單元格移動anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);drawing.createPicture(anchor, index);}
}
- 寫入時引用
EasyExcel.write("Excel文件名稱.xlsx", DemoData.class).sheet("測試").registerWriteHandler(new ImageCellWriteHandler()).doWrite(new DemoData());
環境:
- JDK:1.8.0_202
- SpringBoot:2.7.17
- EasyExcel:3.3.2