Lyscms+
登录注册
Java对多个word文档合并支持图片、图表、图形、表格等
原创Java对多个word文档合并支持图片、图表、图形、表格等

Java
image
亮月官方
2020-10-14 16:42:59
阅读数:880
word合并java word合并word处理

方案一

使用 Free Spire.Doc for Java 免费版本的依赖jar包

  1. 官网上下载获取,解压后将lib文件夹下的Spire.Doc.jar导入IDEA中或者也可以通过maven仓库来安装产品及导入相应依赖。

  2. 示例代码:

    ​ a. 方式1:使用Document类中的insertTextFromFile方法将不同的文档合并到同一个文档。需要注意的是,被合并文档的内容默认从新的一页开始显示。

    import com.spire.doc.Document;
    import com.spire.doc.FileFormat;
    
    public class MergeFiles1 {
        public static void main(String[] args) {
            //获取第一个文档的路径
            String filePath1 = "D:\\frist_file.docx";
            //获取第二个文档的路径
            String filePath2 = "D:\\second_file.docx";
    
            //加载第一个文档
            Document document = new Document(filePath1);
    
            //使用insertTextFromFile方法将第二个文档的内容插入到第一个文档
            document.insertTextFromFile(filePath2, FileFormat.Docx_2013);
    
            //保存文档
            document.saveToFile("D:\\Output\\MergeFiles1.docx", FileFormat.Docx_2013);
        }
    }
    

    b. 方式二: 若需将新加入的文档承接到上一个文档的最后一个段落末尾,则可以使用下面的方法获取第一个文档的最后一个section,然后将被合并文档的正文作为新的段落添加到section

    import com.spire.doc.Document;
    import com.spire.doc.DocumentObject;
    import com.spire.doc.FileFormat;
    import com.spire.doc.Section;
    
    public class MergeFiles2 {
        public static void main(String[] args) {
            //获取第一个文档的路径
            String filePath1 = "D:\\frist_file.docx";
            //获取第二个文档的路径
            String filePath2 = "D:\\second_file.docx";
    
            //加载第一个文档
            Document document1 = new Document(filePath1);
            //加载第二个文档
            Document document2 = new Document(filePath2);
    
            //获取第一个文档的最后一个section
            Section lastSection = document1.getLastSection();
    
            //将第二个文档的段落作为新的段落添加到第一个文档的最后一个section
            for (Section section:(Iterable <Section>)document2.getSections()) {
                for (DocumentObject obj:(Iterable <DocumentObject>)section.getBody().getChildObjects()
                ) {
                    lastSection.getBody().getChildObjects().add(obj.deepClone());
                }
            }
    
            //保存文档
            document1.saveToFile("D:\\Output\\MergeFiles2.docx", FileFormat.Docx_2013);
    
        }
    }
    

方案二

Apache POI是一个开源的利用Java读写Excel、WORD等微软OLE2组件文档的项目,是一个很强大的开源工具包,

这里就介绍一下使用poi进行两个甚至多个word文档的合并,兼容图片, 图表、表格需自行根据情况兼容

/**
     * 合并文件
     *
     * @param firstInputStream
     * @param secondInputStream
     * @param outputFile
     * @throws Exception
     */
    private static void mergeWord(InputStream firstInputStream, InputStream secondInputStream, File outputFile) throws Exception {

        OPCPackage src1Package = null;
        OPCPackage src2Package = null;
        OutputStream dest = new FileOutputStream(outputFile);
        try {
            src1Package = OPCPackage.open(firstInputStream);
            src2Package = OPCPackage.open(secondInputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
        XWPFDocument src1Document = new XWPFDocument(src1Package);
        XWPFDocument src2Document = new XWPFDocument(src2Package);
        appendBody(src1Document, src2Document);
        src1Document.write(dest);
    }

    /**
     * 把文件中的图片拿出来
     *
     * @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(), Document.PICTURE_TYPE_PNG);
            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进行替换 z
            // 下面注释掉的方式会发生图片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);
    }

	/**
	 * 测试合并文档
	 */
    public static void main(String[] args) throws Exception {
        InputStream in1 = null;
        InputStream in2 = null;
        File dest = new File("D:\\dest_out1.docx");
        try {
            in1 = new FileInputStream("D:\\first_file.docx");
            in2 = new FileInputStream("D:\\second_file.docx");
        } catch (Exception e) {
            e.printStackTrace();
        }
        mergeWord(in1, in2, dest);
    }

方案三(建议)

poi-tl(poi template language)是Word模板引擎,基于Microsoft Word模板和数据生成新的文档。

在文档的任何地方做任何事情(Do Anything Anywhere)是poi-tl的星辰大海

方案 移植性 功能性 易用性
Poi-tl Java跨平台 Word模板引擎 基于Apache POI
Apache POI Java跨平台 Apache项目,功能丰富 文档不全,这里有一个教程:Apache POI Word快速入门
Freemarker XML跨平台 仅支持文本,很大的局限性 复杂,需要维护XML结构,代码不可维护
OpenOffice 部署OpenOffice软件,移植性较差 - 复杂,需要了解OpenOffice的API
HTML浏览器导出 依赖浏览器的实现,移植性较差 HTML不能很好的兼容Word的格式 -
Jacob、winlib Windows平台 - 复杂,完全不推荐使用

Apache POI不仅在上层封装了易用的文档API(文本、图片、表格、页眉、页脚、图表等),也可以在底层直接操作文档XML结构,poi-tl正是一个基于Apache POI的Word模板引擎,并且拥有着让人喜悦的特性。

  1. 导入依赖,注意和已有的poi版本冲突解决

    <dependency>
        <groupId>com.deepoove</groupId>
        <artifactId>poi-tl</artifactId>
        <version>1.8.2</version>
    </dependency>
    
  2. 准备一个基础word文件(基础模板文件:在需要合并的多个文件中选择一个作为基础文件),

    image-20201014162920372

  3. 代码使用示例

    public static void main(String[] args) throws IOException {
        XWPFTemplate template = XWPFTemplate.compile("D:\\基础模板文件.docx").render(
        new HashMap<String, Object>(){{
            put("title", "Hi, poi-tl Word模板引擎");
            put("docx_word1", new DocxRenderData(new File("D:\\追加文档1.docx")));
            put("docx_word2", new DocxRenderData(new File("D:\\追加文档2.docx")));
        }});
    
        FileOutputStream out = new FileOutputStream("D:\\poi_tl_output.docx");
        template.write(out);
        out.flush();
        out.close();
        template.close();
    }
    

一个人在年轻的时候浪费自己的才华与天赋是一件非常可惜的事情,文章最后更新于:2020-10-14 16:42:59

flutter简单集成Paypal支付
BeanUtils字段复制工具如何忽略null值字段
loading