EasyPoi可以很方便的通过一个word模板,然后通过填充模板的方式生成我们想要的word文档。但是碰到了一个单模板生成多页数据的场景,比如一个订单详情信息模板,但是有很多订单,需要导入到一个word里面,提供给用户下载这个word文档。这就需要进行word合并了,Easypoi可以生成多个XWPFDocumenmt,我们将它合并成一个就行了。
    特意找了下Easypoi官方文档,没有看到多个word合并的案例,官方文档还是对于单模板生成多页的说明还是空白的,链接为:https://opensource.afterturn.cn/doc/easypoi.html#602。因此特意在网络上了找了word合并相关的博客,通过我自己的一些改动,实现了想要的功能。记录下该工具类,方便后续查阅。

一、合并word文档工具类代码

import org.apache.poi.xwpf.usermodel.Document;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFPictureData;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
import org.springframework.util.CollectionUtils; import java.util.HashMap;
import java.util.List;
import java.util.Map; /**
* WordUtils
*
* @author ZENG.XIAO.YAN
* @version 1.0
* @Date 2019-09-20
*/
public final class WordUtils { /**
* word文件合并
* @param wordList
* @return
* @throws Exception
*/
public static XWPFDocument mergeWord(List<XWPFDocument> wordList) throws Exception{
if (CollectionUtils.isEmpty(wordList)) {
throw new RuntimeException("待合并的word文档list为空");
}
XWPFDocument doc = wordList.get(0);
int size = wordList.size();
if (size > 1) {
doc.createParagraph().setPageBreak(true);
for (int i = 1; i < size; i++) {
// 从第二个word开始合并
XWPFDocument nextPageDoc = wordList.get(i);
// 最后一页不需要设置分页符
if (i != (size-1)) {
nextPageDoc.createParagraph().setPageBreak(true);
}
appendBody(doc, nextPageDoc);
}
}
return doc;
} private 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(), Document.PICTURE_TYPE_PNG);
map.put(before, after);
}
appendBody(src1Body, src2Body,map); } 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进行替换
for (Map.Entry<String, String> set : map.entrySet()) {
addPart = addPart.replace(set.getKey(), set.getValue());
}
}
//将两个文档的xml内容进行拼接
CTBody makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix);
src.set(makeBody);
} }
84
84
 
1
import org.apache.poi.xwpf.usermodel.Document;
2
import org.apache.poi.xwpf.usermodel.XWPFDocument;
3
import org.apache.poi.xwpf.usermodel.XWPFPictureData;
4
import org.apache.xmlbeans.XmlOptions;
5
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
6
import org.springframework.util.CollectionUtils;
7

8
import java.util.HashMap;
9
import java.util.List;
10
import java.util.Map;
11

12
/**
13
 * WordUtils
14
 *
15
 * @author ZENG.XIAO.YAN
16
 * @version 1.0
17
 * @Date 2019-09-20
18
 */
19
public final class WordUtils {
20

21
    /**
22
     * word文件合并
23
     * @param wordList
24
     * @return
25
     * @throws Exception
26
     */
27
    public static  XWPFDocument mergeWord(List<XWPFDocument> wordList) throws Exception{
28
        if (CollectionUtils.isEmpty(wordList)) {
29
            throw  new RuntimeException("待合并的word文档list为空");
30
        }
31
        XWPFDocument doc = wordList.get(0);
32
        int size = wordList.size();
33
        if (size > 1) {
34
            doc.createParagraph().setPageBreak(true);
35
            for (int i = 1; i < size; i++) {
36
                // 从第二个word开始合并
37
                XWPFDocument nextPageDoc = wordList.get(i);
38
                // 最后一页不需要设置分页符
39
                if (i != (size-1)) {
40
                    nextPageDoc.createParagraph().setPageBreak(true);
41
                }
42
                appendBody(doc, nextPageDoc);
43
            }
44
        }
45
        return doc;
46
    }
47

48
    private static void appendBody(XWPFDocument src, XWPFDocument append) throws Exception {
49
        CTBody src1Body = src.getDocument().getBody();
50
        CTBody src2Body = append.getDocument().getBody();
51
        List<XWPFPictureData> allPictures = append.getAllPictures();
52
        // 记录图片合并前及合并后的ID
53
        Map<String,String> map = new HashMap<>();
54
        for (XWPFPictureData picture : allPictures) {
55
            String before = append.getRelationId(picture);
56
            //将原文档中的图片加入到目标文档中
57
            String after = src.addPictureData(picture.getData(), Document.PICTURE_TYPE_PNG);
58
            map.put(before, after);
59
        }
60
        appendBody(src1Body, src2Body,map);
61

62
    }
63

64
    private static void appendBody(CTBody src, CTBody append,Map<String,String> map) throws Exception {
65
        XmlOptions optionsOuter = new XmlOptions();
66
        optionsOuter.setSaveOuter();
67
        String appendString = append.xmlText(optionsOuter);
68
        String srcString = src.xmlText();
69
        String prefix = srcString.substring(0,srcString.indexOf(">")+1);
70
        String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
71
        String sufix = srcString.substring( srcString.lastIndexOf("<") );
72
        String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
73
        if (map != null && !map.isEmpty()) {
74
            //对xml字符串中图片ID进行替换
75
            for (Map.Entry<String, String> set : map.entrySet()) {
76
                addPart = addPart.replace(set.getKey(), set.getValue());
77
            }
78
        }
79
        //将两个文档的xml内容进行拼接
80
        CTBody makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix);
81
        src.set(makeBody);
82
    }
83

84
}

二、使用案例

  使用套路:
    (1)通过easypoi生成word文档放在一个List集合中
    (2)将List集合中的word文档合并成一个
    (3)将合成后的word输出到文件
参考下面图片:
        

防止图片失效,代码也贴上来了
    List<XWPFDocument> wordList = new ArrayList<>();
// 1.通过easypoi生成word文档并放在集合里
for (int i = 0; i < studInocCardVOS.size(); i++) {
ExportStudInocCardVO studInocCardVO = studInocCardVOS.get(i);
Map<String, Object> map = new HashMap<String, Object>();
map.put("birthday", studInocCardVO.getBirthday());
map.put("mobile", studInocCardVO.getMobile());
map.put("mother", studInocCardVO.getMother());
map.put("schoolName", studInocCardVO.getSchoolName());
map.put("className", studInocCardVO.getClassName());
map.put("corpName", studInocCardVO.getCorpName());
map.put("bactNo", studInocCardVO.getBactNo());
map.put("validity", studInocCardVO.getValidity());
map.put("standard", studInocCardVO.getStandard());
map.put("dosage", studInocCardVO.getDosage());
map.put("sex", studInocCardVO.getSex());
map.put("inocDate", studInocCardVO.getInocDate());
// 通过easyPoi生成word文档(即XWPFDocument)
XWPFDocument doc = WordExportUtil.exportWord07(
"接种管理-接种凭证(详细).docx", map);
wordList.add(doc);
}
// 2.把集合里面的word文档全部合并在一个文档
XWPFDocument word = WordUtils.mergeWord(wordList);
File outDir = new File("c:/excel");
if (!outDir.exists()) {
outDir.mkdirs();
}
// 3.将合并后的word文档输出到文件
FileOutputStream fos = new FileOutputStream(new File(outDir, "接种管理-接种凭证-导出(详细).docx"));
word.write(fos);
fos.close();
32
32
 
1
    List<XWPFDocument> wordList = new ArrayList<>();
2
    // 1.通过easypoi生成word文档并放在集合里
3
    for (int i = 0; i < studInocCardVOS.size(); i++) {
4
        ExportStudInocCardVO studInocCardVO = studInocCardVOS.get(i);
5
        Map<String, Object> map = new HashMap<String, Object>();
6
        map.put("birthday", studInocCardVO.getBirthday());
7
        map.put("mobile", studInocCardVO.getMobile());
8
        map.put("mother", studInocCardVO.getMother());
9
        map.put("schoolName", studInocCardVO.getSchoolName());
10
        map.put("className", studInocCardVO.getClassName());
11
        map.put("corpName", studInocCardVO.getCorpName());
12
        map.put("bactNo", studInocCardVO.getBactNo());
13
        map.put("validity", studInocCardVO.getValidity());
14
        map.put("standard", studInocCardVO.getStandard());
15
        map.put("dosage", studInocCardVO.getDosage());
16
        map.put("sex", studInocCardVO.getSex());
17
        map.put("inocDate", studInocCardVO.getInocDate());
18
        // 通过easyPoi生成word文档(即XWPFDocument)
19
        XWPFDocument doc = WordExportUtil.exportWord07(
20
                "接种管理-接种凭证(详细).docx", map);
21
        wordList.add(doc);
22
    }
23
    // 2.把集合里面的word文档全部合并在一个文档
24
    XWPFDocument word = WordUtils.mergeWord(wordList);
25
    File outDir = new File("c:/excel");
26
    if (!outDir.exists()) {
27
        outDir.mkdirs();
28
    }
29
    // 3.将合并后的word文档输出到文件
30
    FileOutputStream fos = new FileOutputStream(new File(outDir, "接种管理-接种凭证-导出(详细).docx"));
31
    word.write(fos);
32
    fos.close();

三、小结

    实现单模板生成多页文件关键点如下:
        (1)通过Easypoi生成word文档
        (2)通过原生的Poi的api进行word合并

Easypoi实现单模板生成多页wrod文档的更多相关文章

  1. 根据Excel的内容和word模板生成对应的word文档

    Sub setname() Dim I As Integer Dim pspname As String Dim pspnumber As String Dim path As String Dim ...

  2. ASP.NET MVC 解析模板生成静态页一(RazorEngine)

    简述 Razor是ASP.NET MVC 3中新加入的技术,以作为ASPX引擎的一个新的替代项.在早期的MVC版本中默认使用的是ASPX模板引擎,Razor在语法上的确不错,用起来非常方便,简洁的语法 ...

  3. NET MVC RazorEngine 解析模板生成静态页

    ASP.NET MVC 解析模板生成静态页一(RazorEngine) 简述 Razor是ASP.NET MVC 3中新加入的技术,以作为ASPX引擎的一个新的替代项.在早期的MVC版本中默认使用的是 ...

  4. powerdesigner连接postgresql数据库生成pdm及word文档

    1.准备软件: powerdesigner165与postgresql的驱动:psqlodbc_11_01_0000 2.安装并破解完成powerdesigner165 参看链接:https://ww ...

  5. 使用apidoc 生成Restful web Api文档

    在项目开发过程中,总会牵扯到接口文档的设计与编写,之前使用的都是office工具,写一个文档,总也是不够漂亮和直观.好在git上的开源大神提供了生成文档的工具,so来介绍一下! 该工具是Nodejs的 ...

  6. 【工具篇】利用DBExportDoc V1.0 For MySQL自动生成数据库表结构文档

    对于DBA或开发来说,如何规范化你的数据库表结构文档是灰常之重要的一件事情.但是当你的库,你的表排山倒海滴多的时候,你就会很头疼了. 推荐一款工具DBExportDoc V1.0 For MySQL( ...

  7. 基于数据库的自动化生成工具,自动生成JavaBean、数据库文档、框架代码等(v5.8.8版)

    TableGo v5.8.8版震撼发布,此次版本更新如下:          1.新增两个扩展字段,用于生成自定义模板时使用.          2.自定义模板新增模板目录,可以选择不同分类目录下的模 ...

  8. SpringBoot入门教程(二十)Swagger2-自动生成RESTful规范API文档

    Swagger2 方式,一定会让你有不一样的开发体验:功能丰富 :支持多种注解,自动生成接口文档界面,支持在界面测试API接口功能:及时更新 :开发过程中花一点写注释的时间,就可以及时的更新API文档 ...

  9. 使用 powerdesigner 将数据库表结构逆向工程生成对应的word文档

    本机系统win10 + mysql 5.7.17 + powerDesigner 16.5 + mysql-connector-odbc-5.3.9-winx32.msi 1 使用 PowerDesi ...

随机推荐

  1. 代码审计-strcmp比较字符串

    <?php $flag = "flag{xxxxx}"; if (isset($_GET['a'])) { if (strcmp($_GET['a'], $flag) == ...

  2. 2019.6.11_MySQL进阶三:临时表

    临时表 临时表主要应用于保存一些临时数据.临时表只在当前连接可见.当关闭连接时,MySQL会自动删除表并且释放空间.临时表在MySQL 3.23版本中添加,低于 3.23版本就无法使用MySQL的临时 ...

  3. Artificial Intelligence in Finance

    https://sigmoidal.io/real-applications-of-ai-in-finance/ Artificial Intelligence is taking the finan ...

  4. maxima已知方程,计算结果

  5. day 29

    Let the dead have the immortality of fame, but the living the immortality of love. 让逝者拥有不朽的荣誉,让生者拥有不 ...

  6. 【转】Java代码编译过程简述

    转载:https://blog.csdn.net/fuzhongmin05/article/details/54880257. 代码编译是由Javac编译器来完成,流程如下图1所示: 图1 Javac ...

  7. [POI2011]Lightening Conductor(决策单调性)

    好久没写过决策单调性了. 这题其实就是 $p_i=\lceil\max\limits_{j}(a_j-a_i+\sqrt{|i-j|})\rceil$. 拆成两边,先只考虑 $j<i$,然后反过 ...

  8. Canal订阅binlog变更并结合kafka实现消息缓冲

    阿里Canal项目请先了解:canal 考虑可能binlog大批量变更,如果直接通过Canal订阅binlog变动,会造成CanalClient会瞬间爆掉.为了解决这个问题,我们可以引入kafka做一 ...

  9. Java代码中对IP进行白名单验证

    来自:https://www.cnblogs.com/shinubi/p/6723003.html public class ipUtil { // IP的正则,这个正则不能验证第一组数字为0的情况 ...

  10. Windows / Office - KMS激活

    Windows / Office - KMS激活 支持Windows操作系统,支持Office软件:包括Windows 10,Office 2016:包括VL版本和MSDN版. (UPDATE: Of ...