EXCEL 图片处理工具类,支持wps中内嵌图片和浮动图片的处理

文章介绍了用于处理WPS中内嵌和浮动图片的Java工具类,通过easyExcel解析Excel文件,提取图片位置信息,支持MultipartFile类型的数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

EXCEL 图片处理工具类,支持wps中内嵌图片和浮动图片的处理,本工具类使用easyExcel 实现excel中图片中获取,返回对应图片的位置.

EXCEL 图片处理工具类,支持wps中内嵌图片和浮动图片的处理

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.XML;
import com.zeekr.vidpspecialmanagerserver.model.excel.WpsImg;
import com.zeekr.vidpspecialmanagerserver.model.excel.wps.CellImageRels;
import com.zeekr.vidpspecialmanagerserver.model.excel.wps.CellImages;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * @author :duanyi
 * @date :Created 2023/9/21
 * @description:
 */

public class WpsImgUtil {

    /**
     * 获取wps中的图片
     * 包括嵌入形式图片和浮动形式图片
     * <p>
     * 嵌入形式图片返回方式:
     * 以map方式返回
     * 键为行列格式 =DISPIMG("ID",1) 字符串
     * <p>
     * 浮动形式图片返回方式:
     * 以map方式返回
     * 键为行列格式 x-y  字符串
     *
     * @param dispStrList
     * @param simpleFile
     * @return
     * @throws IOException
     */
    @SneakyThrows(IOException.class)
    public static Map<String, WpsImg> getWpsImgs(List<String> dispStrList, MultipartFile simpleFile) {
        List<WpsImg> wpsImgList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(dispStrList)) {
            for (String dispStr : dispStrList) {
                if (Objects.nonNull(dispStr) && dispStr.startsWith("=DISPIMG")) {
                    int start = dispStr.indexOf("\"");
                    int end = dispStr.lastIndexOf("\"");
                    if (start != -1 && end != -1) {
                        String imgId = dispStr.substring(start + 1, end);
                        WpsImg wpsImg = new WpsImg();
                        wpsImg.setType(0);
                        wpsImg.setImgId(imgId);
                        wpsImg.setCellStr(dispStr);
                        wpsImgList.add(wpsImg);
                    }
                }
            }
        }

        ZipInputStream zis = new ZipInputStream(simpleFile.getInputStream());
        try {
            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                try {
                    final String fileName = entry.getName();
                    if (Objects.equals(fileName, "xl/cellimages.xml")) {
                        String content = IOUtils.toString(zis, CharsetUtil.UTF_8);
                        JSONObject js = XML.toJSONObject(content);
                        CellImages cellImages = com.alibaba.fastjson2.JSONObject.parseObject(js.toString(), CellImages.class);
                        for (CellImages.CellImagesDTO.CellImageDTO cellImageDTO : cellImages.getCellImages().getCellImage()) {
                            String name = cellImageDTO.getPic().getNvPicPr().getCNvPr().getName();
                            String embed = cellImageDTO.getPic().getBlipFill().getBlip().getEmbed();
                            List<WpsImg> wpsImg = wpsImgList.stream().filter(i -> Objects.equals(i.getImgId(), name)).collect(Collectors.toList());
                            if (CollectionUtil.isNotEmpty(wpsImg)) {
                                wpsImg.forEach(k -> k.setRId(embed));
                            }
                        }
                    }
                } finally {
                    zis.closeEntry();
                }
            }
        } finally {
            zis.close();
        }
        ZipInputStream fzis = new ZipInputStream(simpleFile.getInputStream());
        try {
            ZipEntry entry;
            while ((entry = fzis.getNextEntry()) != null) {
                try {
                    final String fileName = entry.getName();
                    if (Objects.equals(fileName, "xl/_rels/cellimages.xml.rels")) {
                        String content = IOUtils.toString(fzis, CharsetUtil.UTF_8);
                        JSONObject js = XML.toJSONObject(content);
                        CellImageRels cellImageRels = com.alibaba.fastjson2.JSONObject.parseObject(js.toString(), CellImageRels.class);
                        for (CellImageRels.RelationshipsDTO.RelationShipDTO relationshipDTO : cellImageRels.getRelationShips().getRelationship()) {
                            String id = relationshipDTO.getId();
                            String target = "/xl/" + relationshipDTO.getTarget();
                            List<WpsImg> wpsImgs = wpsImgList.stream().filter(i -> Objects.equals(i.getRId(), id)).collect(Collectors.toList());
                            if (CollectionUtil.isNotEmpty(wpsImgs)) {
                                wpsImgs.forEach(k -> k.setImgName(target));
                            }
                        }
                    }
                } finally {
                    fzis.closeEntry();
                }
            }
        } finally {
            fzis.close();
        }

        Workbook workbook = WorkbookFactory.create(simpleFile.getInputStream());
        List<XSSFPictureData> allPictures = (List<XSSFPictureData>) workbook.getAllPictures();
        for (XSSFPictureData pictureData : allPictures) {
            PackagePartName partName = pictureData.getPackagePart().getPartName();
            URI uri = partName.getURI();
            List<WpsImg> wpsImgs = wpsImgList.stream().filter(i -> Objects.equals(i.getImgName(), uri.toString())).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(wpsImgs)) {
                wpsImgs.forEach(k -> k.setPictureData(pictureData));
            }
        }
        Map<String, WpsImg> result = new HashMap<>();
        for (WpsImg wpsImg : wpsImgList) {
            result.put(wpsImg.getCellStr(), wpsImg);
        }
        XSSFSheet sheet = (XSSFSheet) workbook.getSheetAt(0);
        Map<String, WpsImg> flotPictures = WpsImgUtil.getFlotPictures(sheet);
        result.putAll(flotPictures);
        return result;
    }

    /**
     * 获取浮动形式的图片
     * 以map方式返回
     * 键为行列格式 x-y
     *
     * @param xssfSheet
     * @return
     */
    public static Map<String, WpsImg> getFlotPictures(XSSFSheet xssfSheet) {
        Map<String, WpsImg> map = new HashMap<>();
        XSSFDrawing drawingPatriarch = xssfSheet.getDrawingPatriarch();
        if (Objects.isNull(drawingPatriarch)) {
            return map;
        }
        List<XSSFShape> list = drawingPatriarch.getShapes();
        for (XSSFShape shape : list) {
            XSSFPicture picture = (XSSFPicture) shape;
            XSSFClientAnchor xssfClientAnchor = (XSSFClientAnchor) picture.getAnchor();
            XSSFPictureData pdata = picture.getPictureData();
            // 行号-列号
            String key = xssfClientAnchor.getRow1() + "-" + xssfClientAnchor.getCol1();
            WpsImg wpsImg = new WpsImg();
            String partName = pdata.getPackagePart().getPartName().getName();
            String contentType = pdata.getPackagePart().getContentType();
            int lastSlashIndex = partName.lastIndexOf('/');
            wpsImg.setImgName(partName.substring(lastSlashIndex + 1));
            wpsImg.setPictureData(pdata);
            wpsImg.setType(1);
            map.put(key, wpsImg);
        }
        return map;
    }
}


@NoArgsConstructor
@AllArgsConstructor
@Data
public class WpsImg {
    /**
     * 图片id ,wps 内嵌图片后面的id,=DISPIMG后面
     */
    private String imgId;
    /**
     * cellStr,=DISPIMG 字符串
     */
    private String cellStr;
    /**
     * 引用id
     */
    private String rId;
    /**
     * imgName
     */
    private String imgName;
    /**
     * 图片数据
     */
    private XSSFPictureData pictureData;

    /**
     * 0 嵌入单元格 1 浮动
     */
    private int type;
}



@NoArgsConstructor
@AllArgsConstructor
@Data
public class CellImageRels {

    @JSONField(name = "Relationships")
    private RelationshipsDTO relationShips;

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class RelationshipsDTO {
        @JSONField(name = "Relationship")
        private List<RelationShipDTO> relationship;


        @NoArgsConstructor
        @AllArgsConstructor
        @Data
        public static class RelationShipDTO {
            @JSONField(name = "Id")
            private String id;
            @JSONField(name = "Target")
            private String target;
        }
    }
}


@NoArgsConstructor
@AllArgsConstructor
@Data
public class CellImages {

    @JSONField(name = "etc:cellImages")
    private CellImagesDTO cellImages;

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class CellImagesDTO {
        @JSONField(name = "etc:cellImage")
        private List<CellImageDTO> cellImage;

        @NoArgsConstructor
        @AllArgsConstructor
        @Data
        public static class CellImageDTO {
            @JSONField(name = "xdr:pic")
            private PicDTO pic;

            @NoArgsConstructor
            @AllArgsConstructor
            @Data
            public static class PicDTO {
                @JSONField(name = "xdr:nvPicPr")
                private NvPicPrDTO nvPicPr;
                @JSONField(name = "xdr:blipFill")
                private BlipFillDTO blipFill;

                @NoArgsConstructor
                @AllArgsConstructor
                @Data
                public static class NvPicPrDTO {
                    @JSONField(name = "xdr:cNvPr")
                    private CNvPrDTO cNvPr;

                    @NoArgsConstructor
                    @AllArgsConstructor
                    @Data
                    public static class CNvPrDTO {
                        private String name;
                    }
                }

                @NoArgsConstructor
                @AllArgsConstructor
                @Data
                public static class BlipFillDTO {
                    @JSONField(name = "a:blip")
                    private BlipDTO blip;

                    @NoArgsConstructor
                    @AllArgsConstructor
                    @Data
                    public static class BlipDTO {
                        @JSONField(name = "r:embed")
                        private String embed;
                    }
                }
            }
        }
    }
}

<think>嗯,用户的问题是关于在使用Java EasyExcel的afterCellDispose方法时,如何将单元格中的URL转换为图片。我需要先理解这个问题的背景具体需求。 首先,用户提到了EasyExcel导出数据时处理图片的情况,特别是URL类型的数据。他们可能已经在使用EasyExcel导出包含图片Excel文件,但遇到需要将URL动态转换为图片的问题。具体来说,在afterCellDispose方法中实现这个转换。 我需要回忆一下EasyExcel的文档相关API。afterCellDispose是EasyExcel的一个回调方法,在单元格处理完成后触发。用户可能希望在这个阶段获取到单元格中的URL,然后将其转换为图片嵌入到Excel中。但根据我的了解,EasyExcel图片处理通常在写入单元格时就通过特定的数据类(比如ImageData)来处理,而不是在afterCellDispose中转换。 接下来,我需要考虑正确的实现方式。可能用户误解了afterCellDispose的用途,或者想在这里进行动态处理。正确的做法应该是在填充数据时,直接使用ImageData来指定图片,而不是在单元格处理后再转换URL。例如,在数据对象中,将图片字段声明为ImageData类型,并在写入前将URL转换为ImageData实例。 另外,用户提供的引用资料[1][2]提到了使用EasyExcel处理多张图片动态设置单元格宽度。这说明用户可能已经参考了这些资料,但在处理URL转图片时遇到了问题。需要指出,在afterCellDispose中进行图片处理可能不可行,因为此时单元格已经处理完毕,无法再修改内容。正确的时机应该是在数据填充阶段,提前将URL转换为图片数据。 可能还需要考虑如何处理网络图片,比如下载URL对应的图片,并转换为字节数组,然后构造ImageData对象。同时,设置图片的位置缩放比例,确保在Excel中正确显示。此外,动态调整单元格宽度以适应图片数量,这可能需要计算图片的总宽度,并设置合适的列宽。 总结下来,关键点在于正确使用EasyExcel的API,在数据准备阶段转换URL为图片,而不是在afterCellDispose中处理。需要指导用户如何构造包含ImageData的数据对象,并在写入Excel前完成转换。同时,处理可能的异常情况,比如图片下载失败,或者URL无效的情况,确保程序的健壮性。</think>在EasyExcel中通过`afterCellDispose`实现URL转图片需要结合自定义的单元格样式处理器。以下是具体实现步骤: ### 一、核心实现方法 1. **自定义Converter转换器** ```java public class UrlToImageConverter implements Converter<String> { @Override public Class<?> supportJavaTypeKey() { return String.class; } @Override public CellData convertToExcelData(String value, ExcelContentProperty contentProperty) { try { URL url = new URL(value); BufferedImage image = ImageIO.read(url); ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); ImageIO.write(image, "PNG", byteArrayOut); return new CellData(Collections.singletonList( ImageData.builder() .image(byteArrayOut.toByteArray()) .imageType(ImageData.ImageType.PICTURE_TYPE_PNG) .build() )); } catch (Exception e) { return new CellData(value); } } } ``` 2. **注册自定义转换器** ```java EasyExcel.write(fileName, DemoData.class) .registerConverter(new UrlToImageConverter()) .sheet().doWrite(dataList); ``` ### 二、动态列宽配置 当处理多图单元格时,需要通过`CellWriteHandler`动态调整列宽: ```java public class CustomCellWriteHandler implements CellWriteHandler { @Override public void afterCellDispose(CellWriteHandlerContext context) { Cell cell = context.getCell(); if (cell.getCellType() == CellType.STRING && isImageUrl(cell.getStringCellValue())) { int imageCount = getImageCountFromUrl(cell.getStringCellValue()); context.getSheet().setColumnWidth( cell.getColumnIndex(), Math.min(imageCount * 20 * 256, 255*256) // 控制最大宽度255字符 ); } } } ``` ### 三、注意事项 1. **性能优化** - 建议使用`ImageIO.setUseCache(false)`关闭图片缓存[^1] - 对重复URL实施本地缓存机制 - 异步预加载网络图片 2. **异常处理** - 需处理`MalformedURLException``IOException` - 建议设置图片加载超时时间: ```java URLConnection connection = url.openConnection(); connection.setConnectTimeout(5000); connection.setReadTimeout(10000); ``` 3. **样式控制** - 多图排列建议使用: ```java ImageData.builder() .relativeRowHeight(0.5) // 占行高50% .relativeColumnWidth(0.3) // 占列宽30% .relativeFirstHorizontalOffset(0.1) // 水平起始偏移10% .relativeFirstVerticalOffset(0.2) // 垂直起始偏移20% .build(); ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值