easyexcel一个单元格导出多张图片等

1.引入easyexcel依赖


<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel-core</artifactId>
    <version>3.1.2</version>
    <scope>compile</scope>
</dependency>

2.设置自动列宽,我是获取到导出类上注解设置的宽度

/**
 * 自动列宽
 */
@Component
public class AutoColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {

    private static final int MAX_COLUMN_WIDTH = 255;

    //自定义列的列宽
    private Map<String, Integer> zdyColumnWidth = MapUtils.newHashMapWithExpectedSize(20);

    private Map<String,Integer> headNameColumnWidthMap = MapUtils.newHashMapWithExpectedSize(20);

    private final Map<Integer, Map<Integer, Integer>> cache = MapUtils.newHashMapWithExpectedSize(20);

    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell,
                                  Head head,
                                  Integer relativeRowIndex, Boolean isHead) {
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        if (!needSetWidth) {
            return;
        }

        String cellString = cell.toString();
        if (zdyColumnWidth.containsKey(cellString)) {
            writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), zdyColumnWidth.get(cellString) * 256);
            return;
        }

        if (headNameColumnWidthMap.containsKey(cellString)){
            cell.getCellStyle().setWrapText(true);
            writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), headNameColumnWidthMap.get(cellString) * 256);
            return;
        }

        if (true) return ;
        Map<Integer, Integer> maxColumnWidthMap = cache.get(writeSheetHolder.getSheetNo());
        if (maxColumnWidthMap == null) {
            maxColumnWidthMap = new HashMap<Integer, Integer>(16);
            cache.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
        }
        Integer columnWidth = dataLength(cellDataList, cell, isHead);
        if (columnWidth < 0) {
            return;
        }
        if (columnWidth > MAX_COLUMN_WIDTH) {
            columnWidth = MAX_COLUMN_WIDTH;
        }
        Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
        if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
            maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
            writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
        }
    }

    private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        }
        WriteCellData<?> cellData = cellDataList.get(0);
        CellDataTypeEnum type = cellData.getType();
        if (type == null) {
            return -1;
        }
        switch (type) {
            case STRING:
                return cellData.getStringValue().getBytes().length;
            case BOOLEAN:
                return cellData.getBooleanValue().toString().getBytes().length;
            case NUMBER:
                return cellData.getNumberValue().toString().getBytes().length;
            default:
                return -1;
        }
    }

    public void setZdyColumnWidth(Map<String, Integer> zdyColumnWidth,Map<String,Integer> headNameColumnWidthMap) {
        this.zdyColumnWidth = zdyColumnWidth;
        this.headNameColumnWidthMap = headNameColumnWidthMap;
    }
}

 3.将图片导出成超链接的处理类

/**
 * 动态添加超链接
 */
@Component
public class ExcelHyperlinkHandler implements CellWriteHandler {

    public ExcelHyperlinkHandler() {
    }
 

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
                                 Head head, Integer integer, Integer integer1, Boolean isHead) {

    }
 
    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell,
                                Head head, Integer integer, Boolean aBoolean) {
 
    }
 
    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, WriteCellData<?> cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {


    }
 
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

        if (isHead) return ;
        //当前行
        int curRowIndex = cell.getRowIndex();
        //当前列
        int curColIndex = cell.getColumnIndex();

        CellStyle cellStyle = cell.getCellStyle();
        // 设置换行
        cellStyle.setWrapText(true);

        if (curColIndex == 6 || curColIndex == 7 || curColIndex == 8) {
            // 设置字体颜色
            XSSFFont xssfFont = new XSSFFont();
            xssfFont.setColor(IndexedColors.BLUE.index);
            cellStyle.setFont(xssfFont);
            String stringCellValue = cell.getCellType()==CellType.STRING ? cell.getStringCellValue():null;
            if (StringUtils.isNotBlank(stringCellValue)){
                //获得超链接,当前单元格的内容就是一个超链接
                CreationHelper creationHelper = writeSheetHolder.getSheet().getWorkbook().getCreationHelper();
                Hyperlink hyperlink = creationHelper.createHyperlink(HyperlinkType.URL);
                String[] picUrlArr = stringCellValue.split(",");
                // 处理图片超链接
                if (curColIndex == 6){
                    hyperlink.setAddress(picUrlArr[0]);
                    cell.setHyperlink(hyperlink);//添加超链接
                    cell.setCellValue("图片1");
                } else if (curColIndex == 7){
                    if (picUrlArr.length > 1){
                        hyperlink.setAddress(picUrlArr[1]);
                        cell.setHyperlink(hyperlink);//添加超链接
                        cell.setCellValue("图片2");
                    }else{
                        cell.setCellValue("");
                    }
                } else if(curColIndex == 8){
                    if (picUrlArr.length > 2){
                        hyperlink.setAddress(picUrlArr[2]);
                        cell.setHyperlink(hyperlink);//添加超链接
                        cell.setCellValue("图片3");
                    }else{
                        cell.setCellValue("");
                    }
                }
            }
        }
    }

4.设置表格头和内容样式

/*
  EasyExcel 表格样式工具类
*/
@Component
public class SetCellStyle extends AbstractVerticalCellStyleStrategy {

    //表格头样式
    @Override
    protected WriteCellStyle headCellStyle(Head head) {

        WriteCellStyle writeCellStyle = new WriteCellStyle();
        writeCellStyle.setWrapped(true);
        WriteFont font = new WriteFont();
        
        //自定义字体颜色, 默认黑色
        font.setColor(IndexedColors.BLACK.index);

        writeCellStyle.setWriteFont(font);
        return writeCellStyle;
    }


    //单元格格内样式 写法与上面类似
    @Override
    protected WriteCellStyle contentCellStyle(Head head) {

        WriteCellStyle writeCellStyle = new WriteCellStyle();
        WriteFont font = new WriteFont();

        Integer columnIndex = head.getColumnIndex();
        //自定义字体颜色, 默认黑色
        if (columnIndex == 6 || columnIndex == 7 || columnIndex == 8){
            font.setColor(IndexedColors.BLUE.index);
        }else{
            font.setColor(IndexedColors.BLACK.index);
        }
        writeCellStyle.setWriteFont(font);
        writeCellStyle.setWrapped(true);
        return writeCellStyle;
    }

    public static HorizontalCellStyleStrategy getHorizontalStyleStrategy(){
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
//        // 背景设置为灰色
//        headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex());
//        WriteFont headWriteFont = new WriteFont();
//        headWriteFont.setFontHeightInPoints((short)10);
//        // 字体样式
//        headWriteFont.setFontName("Frozen");
//        headWriteFont.setColor(IndexedColors.BLUE.getIndex());
//        headWriteCellStyle.setWriteFont(headWriteFont);
//        //自动换行
//        headWriteCellStyle.setWrapped(true);
//        // 水平对齐方式
//        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
//        // 垂直对齐方式
//        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setWrapped(true);
//        // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
//        //        contentWriteCellStyle.setFillPatternType(FillPatternType.SQUARES);
//        // 背景白色
//        contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
//        // 字体样式
//        WriteFont contentWriteFont = new WriteFont();
//        // 字体大小
//        contentWriteFont.setFontHeightInPoints((short)10);
//        // 字体样式
//        contentWriteFont.setFontName("Calibri");
//        contentWriteFont.setColor(IndexedColors.BLACK.getIndex());
//        contentWriteCellStyle.setWriteFont(contentWriteFont);
//        // 设置边框
//        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
//        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
//        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
//        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

}

5.将图片导出在单元格

/**
 * 生成有图片的excel
 * @param dataList 数据列表
 */
public void generateImageExcel(HttpServletResponse response,List<ReportQuestionReplyExportDTO> dataList) throws Exception {

	if (CollectionUtils.isEmpty(dataList)) return ;
	String fileName = "数据列表";
	String sheetName = "数据列表";

	String picHeadName = "截图";

	//图片列最大图片数
	AtomicReference<Integer> maxImageSize = new AtomicReference<>(0);
	dataList.forEach(item -> {
		String picUrls = item.getReportPicUrl();
		if (StringUtils.isNotEmpty(picUrls)){
			if (picUrls.split(",").length > maxImageSize.get()) {
				maxImageSize.set(picUrls.split(",").length);
			}
		}
	});

	// 表头名称和列宽的映射
	Map<String, Integer> stringIntegerMap = HandleObjDataUtil.headNameColumnWidthMap(ReportQuestionReplyExportDTO.class);
	//设置列长度所用类
	AutoColumnWidthStyleStrategy longWidth = new AutoColumnWidthStyleStrategy();

	Integer partitionSize = dataList.size()/10;
	// 对集合切割分区并发处理
	List<List<ReportQuestionReplyExportDTO>> dataPartition = Lists.partition(dataList, partitionSize > 20? 20 : partitionSize);

	dataPartition.parallelStream().forEach(objList -> {
		objList.forEach(item -> {
			WriteCellData<Void> writeCellData = new WriteCellData<>();
			if (StringUtils.isNotEmpty(item.getReportPicUrl())) {
				//每张图片间距
				Integer splitWidth = -2;
				//每张图片的长度
				Integer imageWidth = 60;
				//图片列的最大长度
				Integer sumWidth = maxImageSize.get() * (imageWidth + splitWidth);

				List<ImageData> imageDataList = new ArrayList<>();
				List<String> imagePathList = Lists.newArrayList(item.getReportPicUrl().split(","));
				for (int i = 1; i <= imagePathList.size(); i++) {
					String path = imagePathList.get(i - 1);
					Integer left = imageWidth * (i - 1) + i * splitWidth;
					Integer right = sumWidth - imageWidth - left;
					ImageData imageData = new ImageData();

					byte[] bytes = null;
					InputStream inputStream = null;
					ByteArrayOutputStream outputStream = null;
					try {
						inputStream = new URL(path).openStream();
						outputStream = new ByteArrayOutputStream();
						// 对图片进行压缩
						Thumbnails.of(inputStream).scale(0.9f).outputQuality(0.3f).toOutputStream(outputStream);
						bytes = outputStream.toByteArray();
					} catch (IOException e) {
						e.printStackTrace();
						continue;
					} finally {
						if (inputStream != null){
							try {
								inputStream.close();
							} catch (IOException e) {
								e.printStackTrace();
							}
						}
					}
					if (bytes.length < 1) continue;

					imageData.setImage(bytes);
					imageData.setImageType(ImageData.ImageType.PICTURE_TYPE_PNG);
					//距离单元格顶部距离
					imageData.setTop(1);
					//距离单元格底部距离
					imageData.setBottom(1);
					//距离单元格左边距离
					imageData.setLeft(left);
					//距离单元格右边距离
					imageData.setRight(right);
					imageData.setAnchorType(ClientAnchorData.AnchorType.MOVE_AND_RESIZE);
					imageDataList.add(imageData);
				}
				writeCellData.setImageDataList(imageDataList);
				

				Map<String, Integer> zdyColumnWidth = new HashMap<>();
				//图片列名称,对应导出对象的列名称,图片列长度
				zdyColumnWidth.put(picHeadName, sumWidth / 6);
				//设置列长度所用类
				longWidth.setZdyColumnWidth(zdyColumnWidth,stringIntegerMap);
			}
			item.setWriteCellDataFile(writeCellData);
		});
	});

	ResponseDataUtils.setExcelRespProp(response,fileName);
	EasyExcel.write(response.getOutputStream())
			.registerWriteHandler(longWidth)
			.head(ReportQuestionReplyExportDTO.class)
			.excelType(ExcelTypeEnum.XLSX)
			.sheet(sheetName)
			.doWrite(dataList);
}

最后由于导出图片太多的话,很慢,导致导出超时,并且excel很大,最后方案改成将图片链接导成excel超链接

<think>好的,用户的问题是想在Java中使用EasyExcel实现同一单元格导出多张图片的功能。我需要先回忆一下EasyExcel的相关文档和功能。根据之前提供的引用,特别是引用[2],里面提到EasyExcel可以添加图片单元格,并且可以设置边距。不过默认情况下,添加图片会填充整个单元格,所以如果要在同一单元格多张图片,可能需要调整图片的位置和大小。 首先,用户的需求是导出多张图片到同一个单元格。我需要确认EasyExcel是否支持在同一个单元格中多次插入图片。根据引用[2],添加图片时可以设置边距,这可能允许通过调整不同图片的位置来并排或堆叠显示。可能需要使用自定义的WriteHandler来处理图片的绘制。 接下来,考虑如何实现。EasyExcel允许注册自定义的WriteHandler,这样可以在单元格渲染时插入内容。可能需要继承CellWriteHandler,并在afterCellCreate方法中操作单元格,添加多个图片。每个图片的位置需要计算,比如水平排列的话,每张图片的起点X坐标递增,Y坐标保持不变。或者垂直排列,调整Y坐标。 另外,图片的尺寸可能需要调整,避免超出单元格。需要根据图片数量和单元格的大小来动态计算每个图片的宽度和高度,或者保持原图比例缩放。例如,如果单元格宽度是100,放两张图片,每张可能占50宽度,或者按比例缩放。 还需要注意EasyExcel的版本,某些功能可能在特定版本中可用。用户提到的引用[3]中提到,使用富文本时需要启用inMemory(true),但图片处理是否也需要这个设置呢?可能需要测试,但根据引用[3],可能不需要,除非涉及到富文本样式。不过,为了确保图片处理正确,可能需要将输出流设置为inMemory模式。 然后,步骤大概是这样的:创建一个自定义的WriteHandler,在afterCellCreate时获取单元格的坐标,然后使用Drawing对象多次添加图片,每次调整图片的位置。例如,第一张图片从(0,0)开始,第二张从(50,0)开始,假设单元格宽度足够。或者根据实际需求调整。 测试时需要注意图片可能覆盖的问题,如果位置计算不正确,后面的图片可能会覆盖前面的。需要确保每个图片的位置和尺寸正确,不会重叠或者超出单元格。 另外,可能需要设置单元格的宽度和高度,以容纳多张图片。例如,调整列宽和行高,使多张图片都能显示出来。否则,单元格默认的大小可能不足以显示所有图片,导致图片被裁剪或重叠。 总结实现步骤:1. 自定义WriteHandler,在afterCellCreate中添加多个图片。2. 计算每个图片的位置和尺寸。3. 注册这个处理器到EasyExcel的写入器。4. 设置合适的单元格尺寸。5. 测试并调整参数。 可能遇到的问题:图片位置计算错误,图片显示不全,单元格尺寸不合适。需要根据实际情况调整参数,或者动态计算图片的位置和大小。例如,根据图片数量平均分配宽度,或者按比例缩放图片以适应单元格。 最后,参考引用[2]中的方法,设置图片的边距和位置,结合多次添加图片到同一个单元格,应该可以实现需求。需要确保每次添加图片时使用正确的坐标,并处理好图片的尺寸,避免覆盖或溢出。</think>在Java中使用EasyExcel实现同一单元格导出多张图片,可通过以下步骤实现: --- ### **步骤说明** 1. **自定义`CellWriteHandler`** 继承`CellWriteHandler`接口,在`afterCellCreate`方法中操作单元格,添加多张图片: ```java public class MultiImageCellHandler implements CellWriteHandler { private List<byte[]> imageDataList; // 图片二进制数据集合 private ImageType imageType; // 图片类型(如PNG) public MultiImageCellHandler(List<byte[]> imageDataList, ImageType imageType) { this.imageDataList = imageDataList; this.imageType = imageType; } @Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { if (cell.getColumnIndex() == targetColumnIndex) { // 目标列 Sheet sheet = writeSheetHolder.getSheet(); Drawing<?> drawing = sheet.createDrawingPatriarch(); for (int i = 0; i < imageDataList.size(); i++) { byte[] imageData = imageDataList.get(i); // 计算图片位置(示例水平排列) int anchorX = i * 100; // 每张图片横向间隔100像素 ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, cell.getColumnIndex(), cell.getRow().getRowNum(), cell.getColumnIndex() + 1, cell.getRow().getRowNum() + 1); drawing.createPicture(anchor, sheet.getWorkbook().addPicture(imageData, imageType.getCode())); } } } } ``` 2. **配置导出参数** 注册自定义处理器,并设置`inMemory(true)`确保富媒体生效[^3]: ```java EasyExcel.write(outputStream, YourDataClass.class) .inMemory(true) // 必须启用内存模式 .registerWriteHandler(new MultiImageCellHandler(imageList, ImageType.PNG)) .sheet("Sheet1") .doWrite(dataList); ``` 3. **调整单元格尺寸** 通过`ColumnWidthStyleStrategy`设置列宽,确保图片可见: ```java .registerWriteHandler(new ColumnWidthStyleStrategy(30)) // 列宽30字符 ``` --- ### **注意事项** - **图片位置计算**:需根据图片数量动态计算`ClientAnchor`的坐标,避免重叠。例如: ```java int anchorX = i * (cellWidth / imageDataList.size()); ``` - **图片压缩**:若图片过多,建议压缩尺寸,避免Excel文件过大。 - **式兼容性**:推荐使用`PNG`式保证透明背景兼容性[^2]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值