You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改Excel单元格后超链接URI编码异常损坏问题求助

解决Apache POI修改Excel后绘图超链接URI编码损坏问题

使用Apache POI的XSSFWorkbook修改Excel指定单元格值后,发现绘图中的内部超链接(如图片链接到其他工作表)被自动进行URI编码,特殊字符(如波兰语字符ż、ę)和空格被替换为编码值(ż→%C5%BC,空格→%20),导致超链接失效,且未修改数据的工作表中的链接也受影响。

源文件drawing.xml.rels中的超链接节点:

<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="#'Rach.Bież. (miesięcznie) '!B3"/>

POI写入后变为:

<Relationship Id="rId2" Target="#'Rach.Bie%C5%BC.%20(miesi%C4%99cznie)%20'!B3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"/>

问题原因

Apache POI在处理XSSF绘图中的超链接时,会默认对Target属性进行URI编码,但Excel自身存储内部工作表链接时,使用的是原始字符而非编码值。POI的编码逻辑与Excel的存储规范不匹配,导致写入时过度编码,破坏原有链接。

解决方案

方案1:遍历并修复绘图超链接的Target值

在写入工作簿前,遍历所有工作表的绘图部件,解码超链接的Target属性,恢复原始字符格式:

import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

// ... 原有读取文件、初始化工作簿的代码 ...

// 修改单元格的逻辑保持不变
XSSFSheet sheet = workbook.getSheet("test");
Row row = sheet.getRow(0);
Cell cell = row.getCell(0);
cell = row.createCell(0);
cell.setCellValue(1.0);

// 遍历所有工作表,修复绘图中的超链接
for (XSSFSheet s : workbook) {
    XSSFDrawing drawing = s.getDrawingPatriarch();
    if (drawing != null) {
        // 获取绘图的超链接关系集合
        PackageRelationshipCollection rels = drawing.getPackagePart().getRelationshipsByType(
            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
        );
        for (PackageRelationship rel : rels) {
            String target = rel.getTargetURI().toString();
            try {
                // 解码Target值,恢复原始字符
                String decodedTarget = URLDecoder.decode(target, "UTF-8");
                // 重新设置Target,避免POI再次编码
                rel.setTargetURI(new java.net.URI(decodedTarget));
            } catch (UnsupportedEncodingException | java.net.URISyntaxException e) {
                e.printStackTrace();
            }
        }
    }
}

// 写入工作簿
ByteArrayOutputStream bos = new ByteArrayOutputStream();
workbook.write(bos);  
bos.close();
workbook.close();

方案2:直接操作OOM修改drawing.xml.rels文件

如果方案1无效,可以直接操作Open XML底层对象模型,修改drawing.xml.rels中的节点:

import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URLDecoder;

// ... 原有读取文件、修改单元格的代码 ...

// 处理绘图rels文件
for (XSSFSheet s : workbook) {
    XSSFDrawing drawing = s.getDrawingPatriarch();
    if (drawing != null) {
        PackagePart relPart = drawing.getPackagePart().getRelationshipsPart();
        if (relPart != null) {
            try {
                // 读取rels文件内容
                InputStream is = relPart.getInputStream();
                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
                Document doc = dBuilder.parse(is);
                is.close();

                // 获取所有超链接关系节点
                NodeList relNodes = doc.getElementsByTagName("Relationship");
                for (int i = 0; i < relNodes.getLength(); i++) {
                    Element relElement = (Element) relNodes.item(i);
                    String type = relElement.getAttribute("Type");
                    if ("http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink".equals(type)) {
                        String target = relElement.getAttribute("Target");
                        // 解码Target
                        String decodedTarget = URLDecoder.decode(target, "UTF-8");
                        relElement.setAttribute("Target", decodedTarget);
                    }
                }

                // 写回rels文件
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                TransformerFactory tf = TransformerFactory.newInstance();
                Transformer transformer = tf.newTransformer();
                DOMSource source = new DOMSource(doc);
                StreamResult result = new StreamResult(os);
                transformer.transform(source, result);
                relPart.setInputStream(new ByteArrayInputStream(os.toByteArray()));
                os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

// 写入工作簿
ByteArrayOutputStream bos = new ByteArrayOutputStream();
workbook.write(bos);  
bos.close();
workbook.close();

方案3:升级Apache POI版本

部分较旧版本的POI存在该编码逻辑问题,尝试升级到最新稳定版(如5.2.5+),查看官方是否已修复该Bug。

验证方法

执行修改后的代码后:

  1. 打开生成的Excel文件,检查btn工作表中的图片超链接是否能正常跳转
  2. 解压Excel文件,查看xl/drawings/_rels/drawing1.xml.rels中的Target值是否恢复为原始未编码格式

内容的提问来源于stack exchange,提问作者Vap Hel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 06:10:15