一个遗留项目批量导出 Word 时,页面选中了不同二维码,文档里却全显示成同一张。代码不是我写的,排查先从传入数据开始,再逐层追到模板。

路径不同,图片开头相同,也不能直接判断重复

最初怀疑列表中重复传入图片路径,调试后排除了。随后看到文件读取与 Base64 转换,两张图片的字节数组开头几十位相同,又一度怀疑读取出错。

继续比较完整内容,发现 Base64 只是开头相近,整体不同。图片格式头相同很常见,判断重复应比较完整内容或摘要,而不能只看前几个字节。

涉及真实二维码时,不应把完整内容无节制地打印到日志;可以对受控样本比较长度和哈希。

读取文件也要正确处理

遗留代码用 available() 分配数组,再调用一次 read。即使它不是当时图片重复的根因,也不能作为通用完整读取方式:available() 不是输入流总长度保证,一次 read 也未必填满数组。Java 8 InputStream

已知是受控的小图片文件时,Java 8 可以使用:

byte[] data = Files.readAllBytes(path);
String base64 = Base64.getEncoder().encodeToString(data);

Files、Path 与 Base64 分别来自 java.nio.file 和 java.util;文件大小仍需限制,大文件不宜全部读入内存。

真正的问题在名称与引用

当时模板采用旧 Word XML/VML 形式,用 w:binData 保存图片数据,以 w:name 命名,再由 v:imagedata 的 src 引用。遗留循环把所有名称写成同一个值,导致文档里不同位置引用了冲突的标识。

修正要点是每张图片使用唯一名称,并让数据定义与引用完全一致。下面只展示已有模板中的相关片段,不是完整 Word 文档:

<#list qrs as qr>
  <w:p>
    <w:r>
      <w:pict>
        <w:binData w:name="${qr.imageName?xml}">${qr.base64}</w:binData>
        <v:shape id="QrShape${qr_index}" style="width:80pt;height:80pt">
          <v:imagedata src="${qr.imageName?xml}"/>
        </v:shape>
      </w:pict>
    </w:r>
  </w:p>
</#list>

qrs 中每项应有唯一的 imageName 和对应 base64。已有模板需保留正确的命名空间及格式要求;shape 标识也不能在其他循环中冲突。FreeMarker list 指令、Word 2003 binData 定义

检查输出中的整条对应关系

用两张明显不同的测试二维码导出,检查名称唯一、src 对应、数据不同,再在目标 Word 环境里确认显示和扫码结果。该处理针对上述旧 XML 模板,不能直接推导为所有 DOCX 图片重复问题的修复;DOCX 的媒体文件和关系还需按实际格式检查。