莫方教程网

专业程序员编程教程与实战案例分享

若依框架中实现Excel浮动图片和WPS内嵌式图片的批量导入功能

“ 本文将详细介绍如何在若依框架中实现Excel浮动图片和WPS内嵌式图片的批量导入功能。”


01 实现浮动图片导入


经由对实体层以及 ExcelUtil.java 的革新,我们能够达成标准 Excel 图片与 WPS 嵌入式图片的导入。

若依框架自身携有浮动图片导入的功能,仅需于项目代码的实体层添加如下代码便可:

cellType = Excel.ColumnType.IMAGE

1.1 在实体层添加图片字段

在项目的实体类(例如`
PerformanceCategoryRuleSystem`)中,为需要导入图片的字段添加注解。例如:

/** 图示1 */

@Excel(name = "图示1", cellType = Excel.ColumnType.IMAGE)

private String image1;

/** 图示2 */

@Excel(name = "图示2", cellType = Excel.ColumnType.IMAGE)

private String image2;

通过以上配置,框架会自动识别这些字段为图片类型,并在导入时处理。

02 实现支持WPS内嵌式图片

2.1.修改ExcelUtil.java工具类

对于WPS内嵌式图片需要对ExcelUtil.java进行改造,保证能够读取Excel文件中嵌入式图片函数“=DISPIMG("ID_C185A741AD754C2C883DA9D8B97156E7",1)”,增加对cellimages.xml和cellimages.xml.rels文件的解析功能。

2.2.导入依赖

在ExcelUtil.java顶部添加以下依赖

import java.io.*;
import java.util.zip.*;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import com.ruoyi.common.utils.uuid.IdUtils;

2.3.修改`importExcel`方法

修改importExcel方法以支持Excel文件流处理和图片解析,代码如下:

2.3.1.在ExcelUtil.java顶部申明变量

/**
* 文件对象
*/
private File file;
/**
* key 函数ID value Rid
*/
private Map<String, String> imageIdMappingMap = new HashMap<>();
/**
* key :Rid value 图片信息
*/
private Map<String, String> imageMap = new HashMap<>();

2.3.2.修改importExcel方法

public List
 importExcel(String sheetName, InputStream is, int titleNum) throws Exception {
    this.type = Type.IMPORT;
    // 将Excel流转换为文件
    this.file = new File(VasConfig.getImportPath() + "/" + IdUtils.fastUUID() + ".xlsx");
    FileUtils.inputStreamToFile(is, this.file);
    // 解析cellimages.xml文件中单元格函数ID与RID的对应关系
    ridWithIDRelationShip();
    // 解析cellimages.xml.rels文件中RID与图片路径的对应关系
    ridWithImagePathRelationShip();
    this.wb = WorkbookFactory.create(new FileInputStream(this.file));
    List
 list = new ArrayList
();
    // 如果指定sheet名,则取指定sheet中的内容,否则默认指向第1个sheet
    Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
    if (sheet == null) {
        throw new IOException("文件sheet不存在");
    }
    // ... 后续逻辑
}

2.4.处理浮动图片和WPS嵌入式图片

在importExcel方法中,添加对浮动图片和WPS嵌入式图片的处理逻辑:

else if (ColumnType.IMAGE == attr.cellType()) {
    // 先尝试处理标准Excel图片
    boolean processedStandardImage = false;
    if (StringUtils.isNotEmpty(pictures)) {
        PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());
        if (image != null) {
            byte[] data = image.getData();
            val = FileUtils.splitImportBytes(data);
            log.info("成功导入标准Excel图片: {} @ {},{}", field.getName(), row.getRowNum(), entry.getKey());
            processedStandardImage = true;
        }
    }
    // 如果没有处理标准图片,则检查并处理WPS嵌入图片
    if (!processedStandardImage) {
        // 获取单元格原始值作为字符串
        Cell rawCell = row.getCell(entry.getKey());
        String cellValue = "";
        if (rawCell != null) {
            if (rawCell.getCellType() == CellType.FORMULA) {
                cellValue = rawCell.getCellFormula();
            } else {
                try {
                    cellValue = rawCell.getStringCellValue();
                } catch (Exception e) {
                    cellValue = this.getCellValue(row, entry.getKey()).toString();
                }
            }
        }
        log.info("检查WPS嵌入图片,单元格值: {}", cellValue);
        // 检查是否包含WPS嵌入图片的特征
        if (isWPSEmbeddedImage(cellValue)) {
            log.info("发现WPS嵌入图片函数: {}", cellValue);
            // 提取图片ID
            String imageId = null;
            for (String keyId : imageIdMappingMap.keySet()) {
                if (cellValue.contains(keyId)) {
                    imageId = keyId;
                    break;
                }
            }
            // 如果没找到精确匹配,尝试根据字符串提取ID
            if (imageId == null) {
                imageId = subImageId(cellValue);
            }
            if (imageId != null) {
                log.info("提取的图片ID: {}", imageId);
                String picPath = getImplantPicById(imageId);
                if (picPath != null) {
                    String fullPath = "xl/" + picPath;
                    log.info("尝试读取图片文件: {}", fullPath);
                    try {
                        InputStream picInputStream = openFile(fullPath);
                        if (picInputStream != null) {
                            byte[] imageData = IOUtils.toByteArray(picInputStream);
                            val = FileUtils.splitImportBytes(imageData);
                            log.info("成功导入WPS嵌入图片: {} -> {}", imageId, val);
                            picInputStream.close();
                        } else {
                            log.error("无法读取图片文件: {}", fullPath);
                        }
                    } catch (Exception e) {
                        log.error("处理WPS嵌入图片失败: {}", e.getMessage(), e);
                    }
                } else {
                    log.error("未找到图片路径对应的图片: {}", imageId);
                }
            } else {
                log.error("无法从单元格值中提取图片ID: {}", cellValue);
            }
        }
    }
}

2.4. 新增解析`cellimages.xml`中单元格函数ID与RID的对应关系

在ExcelUtil.java中新增以下方法:

/**
 * 解析cellimages.xml文件中excel中单元格函数ID对应的RID对应关系
 * @author:
 * @date: 2025/5/14
 * @param: []
 * @return: void
 */
private void ridWithIDRelationShip() throws IOException {
    InputStream inputStream = null;
    try {
        inputStream = openFile("xl/cellimages.xml");
        if (inputStream == null) {
            log.error("无法打开cellimages.xml文件");
            return;
        }
        SAXReader reader = new SAXReader();
        Map
 nsMap = new HashMap<>();
        nsMap.put("etc", "http://www.wps.cn/officeDocument/2017/etCustomData");
        nsMap.put("xdr", "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing");
        nsMap.put("a", "http://schemas.openxmlformats.org/drawingml/2006/main");
        nsMap.put("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
        reader.getDocumentFactory().setXPathNamespaceURIs(nsMap);
        Document dc = reader.read(inputStream);
        Element rootElement = dc.getRootElement();
        List
 cellImageList = rootElement.elements(QName.get("cellImage", Namespace.get("etc", "http://www.wps.cn/officeDocument/2017/etCustomData")));
        log.info("找到 {} 个嵌入图片", cellImageList.size());
        for (Element cellImage : cellImageList) {
            try {
                Element pic = cellImage.element(QName.get("pic", Namespace.get("xdr", "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")));
                Element nvPicPr = pic.element(QName.get("nvPicPr", Namespace.get("xdr", "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")));
                Element cNvPr = nvPicPr.element(QName.get("cNvPr", Namespace.get("xdr", "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")));
                String imageId = cNvPr.attributeValue("name");
                Element blipFill = pic.element(QName.get("blipFill", Namespace.get("xdr", "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")));
                Element blip = blipFill.element(QName.get("blip", Namespace.get("a", "http://schemas.openxmlformats.org/drawingml/2006/main")));
                String imageRid = blip.attributeValue(QName.get("embed", Namespace.get("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships")));
                imageIdMappingMap.put(imageId, imageRid);
                log.info("映射图片ID: {} -> RID: {}", imageId, imageRid);
            } catch (Exception e) {
                log.error("处理图片节点时出错: {}", e.getMessage());
            }
        }
    } catch (Exception e) {
        log.error("解析cellimages.xml文件中excel中单元格函数ID对应的RID对应关系失败", e);
    } finally {
        if (inputStream != null) {
            inputStream.close();
        }
    }
}

2.5.新增解析`cellimages.xml.rels`中RID与图片路径的对应关系

/**
 * 解析cellimages.xml.rels文件中cellimages.xml文件中的RID对应图片信息对应关系
 * @author:
 * @date: 2025/5/14
 * @param: []
 * @return: void
 */
private void ridWithImagePathRelationShip() throws IOException {
    InputStream inputStream = null;
    try {
        inputStream = openFile("xl/_rels/cellimages.xml.rels");
        if (inputStream == null) {
            log.error("无法打开cellimages.xml.rels文件");
            return;
        }
        SAXReader reader = new SAXReader();
        Map
 nsMap = new HashMap<>();
        nsMap.put("", "http://schemas.openxmlformats.org/package/2006/relationships");
        reader.getDocumentFactory().setXPathNamespaceURIs(nsMap);
        Document dc = reader.read(inputStream);
        Element rootElement = dc.getRootElement();
        List
 imageRelationshipList = rootElement.elements("Relationship");
        log.info("找到 {} 个图片关系定义", imageRelationshipList.size());
        for (Element imageRelationship : imageRelationshipList) {
            try {
                String imageRid = imageRelationship.attributeValue("Id");
                String imagePath = imageRelationship.attributeValue("Target");
                this.imageMap.put(imageRid, imagePath);
                log.info("图片路径映射: {} -> {}", imageRid, imagePath);
            } catch (Exception e) {
                log.error("处理图片关系时出错: {}", e.getMessage());
            }
        }
    } catch (Exception e) {
        log.error("处理xml中rid和图片路径映射关系失败: {}", e.getMessage(), e);
    } finally {
        if (inputStream != null) {
            inputStream.close();
        }
    }
}

2.6.新增打开ZIP文件并获取指定文件流

/**
 * @author:
 * @date: 2025/5/14
 * @param: [filePath]
 * @return: java.io.InputStream
 */
private InputStream openFile(String filePath) {
    try {
        ZipFile zipFile = new ZipFile(this.file);
        ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(this.file));
        ZipEntry nextEntry = null;
        while ((nextEntry = zipInputStream.getNextEntry()) != null) {
            String name = nextEntry.getName();
            if (name.equalsIgnoreCase(filePath)) {
                log.info("在ZIP中找到文件: {}", filePath);
                InputStream stream = zipFile.getInputStream(nextEntry);
                zipInputStream.close();
                return stream;
            }
        }
        zipInputStream.close();
        log.error("在ZIP中未找到文件: {}", filePath);
    } catch (Exception e) {
        log.error("解析ZIP文件失败: {}", e.getMessage(), e);
    }
    return null;
}

2.7.新增提取图片ID

/**
 * 提取图片ID
 * @author:
 * @date: 2025/5/14
 * @param: [imageId]
 * @return: String
 */
private String subImageId(String imageId) {
    try {
        int startIndex = imageId.indexOf("ID_");
        if (startIndex == -1) {
            log.error("单元格值中未找到ID_前缀: {}", imageId);
            return null;
        }
        int endIndex = -1;
        for (int i = startIndex; i < imageId.length(); i++) {
            char c = imageId.charAt(i);
            if (c == '"' || c == ',' || c == ')' || c == ' ') {
                endIndex = i;
                break;
            }
        }
        if (endIndex == -1) {
            endIndex = imageId.length();
        }
        String extractedId = imageId.substring(startIndex, endIndex);
        log.info("成功提取图片ID: {}", extractedId);
        return extractedId;
    } catch (Exception e) {
        log.error("提取图片ID时出错: {}", e.getMessage(), e);
        return null;
    }
}

2.8.新增获取嵌入图片路径

/**
 * 根据图片ID获取嵌入图片路径
 * @author:
 * @date: 2025/5/14
 * @param: [imageId]
 * @return: String
 */
private String getImplantPicById(String imageId) throws IOException {
    try {
        String imageRid = imageIdMappingMap.get(imageId);
        if (imageRid == null) {
            log.error("未找到图片ID对应的RID: {}", imageId);
            return null;
        }
        String imagePath = imageMap.get(imageRid);
        if (imagePath == null) {
            log.error("未找到RID对应的图片路径: {}", imageRid);
            return null;
        }
        log.info("成功获取图片路径: {} -> {} -> {}", imageId, imageRid, imagePath);
        return imagePath;
    } catch (Exception e) {
        log.error("获取图片路径时出错: {}", e.getMessage());
        return null;
    }
}

2.9.新增检测WPS嵌入图片

/**
 * 检测单元格是否包含WPS嵌入图片
 * @author:
 * @date: 2025/5/14
 * @param: [cellValue]
 * @return: boolean
 */
private boolean isWPSEmbeddedImage(String cellValue) {
    return cellValue != null &&
           (cellValue.contains("DISPIMG") ||
            cellValue.contains("ID_") &&
            (cellValue.contains("image") || cellValue.contains("media")));
}

03 修改FileUtils.java方法

3.1.在FileUtils.java中新增将输入流写入文件的方法

/**
 * 将输入流写入到文件
 * @param in 输入流
 * @param file 目标文件
 */
public static void inputStreamToFile(InputStream in, File file) throws IOException {
    try {
        Files.copy(in, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
    } catch (Exception e) {
        throw new IOException(e.getMessage(), e);
    } finally {
        IOUtils.closeQuietly(in);
    }
}

总结

通过以上步骤,我们在若依框架中实现了Excel浮动图片和WPS内嵌式图片的批量导入功能。核心在于:

  1. 利用若依框架自带的图片导入功能,配置实体层字段。
  2. 改造ExcelUtil.java,增加对WPS嵌入式图片的解析逻辑。
  3. 新增解析cellimages.xml和cellimages.xml.rels的方法,处理图片ID与路径的映射关系。
  4. 增加文件流处理工具方法,确保Excel文件的正确解析。

这些改造能够有效处理标准Excel图片和WPS特有的内嵌图片,满足复杂的Excel导入需求。

注意:确保在开发环境中测试以上代码,并根据实际需求调整日志输出和异常处理逻辑。


控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言