背景:
要求导出一个word,结果筛选出的打卡记录列表,一条打卡记录一页,里面包含图片。由于第一页的打卡记录前还需要显示工单的统计,所以采用双模板多文件合并的模式,第一页和内容各一个模板。
问题:
一开始是用getDocument().addNewBody().set()进行追加合并,导致图片会重复使用第一页的图片
注:这里写入数据到XWPFDocument用的ParseWord07类是easypoi中的,底层都是poi。for循环中,一个map生成一个文档,都接着headDoc(头部文档)插入内容,实现多个文档拼接成一个
XWPFDocument headDoc = new ParseWord07().parseWord(tempHeadPath, headMap);
headDoc.createParagraph().setPageBreak(true);
for (Map<String, Object> map : exportDataList) {
//合并内容
XWPFDocument contentDoc = new ParseWord07().parseWord(tempContentPath, map);
contentDoc.createParagraph().setPageBreak(true);
headDoc.getDocument().addNewBody().set(contentDoc.getDocument().getBody());
}
导出后发现每条记录的第一张图片都跟第一页的图片一样,多图片的记录除第一张图片能显示之外都没法显示。
解决办法:
记录图片合并前及合并后的ID,并用id匹配追加
XWPFDocument headDoc = new ParseWord07().parseWord(tempHeadPath, headMap);
headDoc.createParagraph().setPageBreak(true);
for (Map<String, Object> map : exportDataList) {
//合并内容
XWPFDocument contentDoc = new ParseWord07().parseWord(tempContentPath, map);
contentDoc.createParagraph().setPageBreak(true);
WordPoiUtils.appendBody(headDoc, contentDoc);
}
WordPoiUtils
/**
* 两个文件对象进行追加
* 备注: 解决word模板多页面导出每页中的图片均显示第一页中的图片
*
* @param src
* @param append
* @throws Exception
*/
public static void appendBody(XWPFDocument src, XWPFDocument append) throws Exception {
CTBody src1Body = src.getDocument().getBody();
CTBody src2Body = append.getDocument().getBody();
List<XWPFPictureData> allPictures = append.getAllPictures();
// 记录图片合并前及合并后的ID
Map<String, String> map = new HashMap<>();
for (XWPFPictureData picture : allPictures) {
String before = append.getRelationId(picture);
//将原文档中的图片加入到目标文档中
String after = src.addPictureData(picture.getData(), picture.getPictureType());
map.put(before, after);
}
appendBody(src1Body, src2Body, map);
}
/**
* 把图片ID替换一下 避免冲突
*
* @param src
* @param append
* @param map
* @throws Exception
*/
private static void appendBody(CTBody src, CTBody append, Map<String, String> map) throws Exception {
XmlOptions optionsOuter = new XmlOptions();
optionsOuter.setSaveOuter();
String appendString = append.xmlText(optionsOuter);
String srcString = src.xmlText();
String prefix = srcString.substring(0, srcString.indexOf(">") + 1);
String mainPart = srcString.substring(srcString.indexOf(">") + 1, srcString.lastIndexOf("<"));
String sufix = srcString.substring(srcString.lastIndexOf("<"));
String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
if (map != null && !map.isEmpty()) {
//对xml字符串中图片ID进行替换
// 下面注释掉的方式会发生图片id冲突
// for (Map.Entry<String, String> set : map.entrySet()) {
// addPart = addPart.replace(set.getKey(), "RE:"+set.getValue());
// }
// addPart = addPart.replaceAll("RE:",");
// 采用正则追加替换方式完美解决
String patter = org.apache.commons.lang.StringUtils.join(map.keySet(), "|");
Pattern compile = Pattern.compile(patter);
Matcher matcher = compile.matcher(addPart);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String value = map.get(matcher.group());
if (value != null) {
matcher.appendReplacement(sb, value);
}
}
matcher.appendTail(sb);
addPart = sb.toString();
}
//将两个文档的xml内容进行拼接
CTBody makeBody = CTBody.Factory.parse(prefix + mainPart + addPart + sufix);
src.set(makeBody);
}