Bootstrap

积累知识库:poi实现word文件合并

基于poi的两种方案实现:

第一种方案:

1.导入依赖

        <dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.9.1</version>
        </dependency>

2.简单api使用


public class TestMergeWord {
    public static void main(String[] args) throws Exception{
        NiceXWPFDocument main = new NiceXWPFDocument(new FileInputStream("C:\\Users\\ab\\Desktop\\study\\12\\4.docx"));
 
        NiceXWPFDocument sub = new NiceXWPFDocument(new FileInputStream("C:\\Users\\ab\\Desktop\\study\\12\\1.docx"));
 
        NiceXWPFDocument design = new NiceXWPFDocument(new FileInputStream("C:\\Users\\ab\\Desktop\\study\\12\\2.docx"));
        NiceXWPFDocument design1 = new NiceXWPFDocument(new FileInputStream("C:\\Users\\ab\\Desktop\\study\\12\\3.docx"));

        // 合并两个文档
        NiceXWPFDocument newDoc = sub.merge(design).merge(design1).merge(main);
        // 生成新文档
        FileOutputStream out = new FileOutputStream("C:\\Users\\ab\\Desktop\\study\\12\\a.docx");
        newDoc.write(out);
        newDoc.close();
        out.close();
        System.out.println("合并word成功!");
    }
}
 

​

3.项目中使用,需要分页加分页符

 public  ResultFile mergeDoc(List<String> fileList,String fileName)  {
        File wordFile = new File(fileName);
        try (OutputStream dest = new FileOutputStream(wordFile)){
            List<NiceXWPFDocument> niceXWPFDocumentList = new LinkedList<>();
            for (String fileId : fileList) {
                //从已经上传到oss的文件获取
                File file = dowmFile(fileId);
                FileInputStream fileInputStream = new FileInputStream(file);
                NiceXWPFDocument merge = new NiceXWPFDocument(fileInputStream);
                niceXWPFDocumentList.add(merge);
            }
            if (CollectionUtils.isEmpty(niceXWPFDocumentList)){
                return null;
            }
            NiceXWPFDocument newDoc = null;
            for (int i = 0; i < niceXWPFDocumentList.size(); i++) {
                if (i==0){
                    newDoc = niceXWPFDocumentList.get(0);
                }else {
                    //添加分页符
                    newDoc.createParagraph().createRun().addBreak(BreakType.PAGE);
                    newDoc =  newDoc.merge(niceXWPFDocumentList.get(i));
                }
            }

            if (newDoc != null){
                newDoc.write(dest);
            }
        } catch (Exception e) {
            log.info("合并出错:",e);
          
        }
        //才能文件上传到oss
        ResultFile resultFile = this.uploadFile(wordFile);
        //删除临时文件
        wordFile.delete();
        return resultFile ;
    }

第二种方案:

若依方式实现:

package com.study.upload.util;
 
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.BreakType;
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.web.multipart.MultipartFile;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
 
/**
 * 参数1:需要合并的word的文件对象list
 * 参数2:合并之后word存储的全路径file对象
 */
public class MergeDocUtils {
 
 
    public static void mergeDoc(MultipartFile[] fileList, File newFile) throws Exception {
 
        OutputStream dest = new FileOutputStream(newFile);
        ArrayList<XWPFDocument> documentList = new ArrayList<XWPFDocument>();
        XWPFDocument doc = null;
        for (int i = 0; i < fileList.length; i++) {
            OPCPackage open = OPCPackage.open(fileList[i].getInputStream());
            XWPFDocument document = new XWPFDocument(open);
            documentList.add(document);
        }

 
        for (int i = 0; i < documentList.size(); i++) {
            doc = documentList.get(0);
            if (i !=  0) {
                //分页
                if (i != documentList.size()-1){
                    documentList.get(i).createParagraph().createRun().addBreak(BreakType.PAGE);
                }
                if (i != 0){
                    appendBody(doc, documentList.get(i));
                }
            }
        }
        doc.write(dest);//输出合并之后的文件
    }
 
 
 
 
    public static void appendBody(XWPFDocument src, XWPFDocument append) throws Exception {
        CTBody src1Body = src.getDocument().getBody();
        CTBody src2Body = append.getDocument().getBody();

      // 添加命名空间
        org.w3c.dom.Element domNode = (org.w3c.dom.Element) src.getDocument().getBody().getDomNode();
        DocumentHelper.addNamespaceDeclaration(domNode,"xsi","http://www.w3.org/2001/XMLSchema-instance");
        // 添加命名空间
        org.w3c.dom.Element domNode2 = (org.w3c.dom.Element) append.getDocument().getBody().getDomNode();
        DocumentHelper.addNamespaceDeclaration(domNode2,"xsi","http://www.w3.org/2001/XMLSchema-instance");

 
        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);
 
        //去掉追加word内容中的 w:sectPr 标签,确保合成的word中只有一个 w:sectPr 标签对
        //避免合成的word文档打开之后会提示有些内容读不出来,导致文件损坏
        String rgex = "<[\\s]*?w:sectPr[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?w:sectPr[\\s]*?>";
        appendString = appendString.replaceAll(rgex, "");
 
        String srcString = src.xmlText();
        String regex = regex(srcString, "w:sectPr");
        System.out.println(regex);
 
        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);
    }
 
    /**
     * 获取指定标签中的内容
     *
     * @param xml
     * @param label
     * @return
     */
    public static String regex(String xml, String label) {
        String context = "";
        // 正则表达式
        String rgex = "<" + label + "[^>]*>((?:(?!<\\/" + label + ">)[\\s\\S])*)<\\/" + label + ">";
        Pattern pattern = Pattern.compile(rgex);// 匹配的模式
        Matcher m = pattern.matcher(xml);
        // 匹配的有多个
        List<String> list = new ArrayList<String>();
        while (m.find()) {
            int i = 1;
            list.add(m.group(i));
            i++;
        }
        if (list.size() > 0) {
            // 输出内容自己定义
            context = String.valueOf(list.size());
        }
        return context;
    }
 
}
 

3.调用

    @PostMapping("/mer")
    public Boolean mer(@RequestParam("file") MultipartFile[] files) throws Exception {

         //new一个list 模拟要合并的word对象集合
        List<InputStream>docFileList = new ArrayList<>();
        docFileList.add(files[0].getInputStream());
        docFileList.add(files[1].getInputStream());

        //合并之后doc存储路径 此处读的配置文件的存储路径 D:/pdfData/
        String docPath = "C:\\Users\\ab\\Desktop\\olddata\\1\\";
        //当前日期+UUID作为文件名防止重复
        String fileName = LocalDate.now() + "-" + UUID.randomUUID().toString().replaceAll("-", "");
        //合并之后doc存储路径
        String mergeDocUrl = docPath+fileName+".docx";
        //转成file对象
        File mergeDocFile = new File(mergeDocUrl);
        //合并doc
        MergeDocUtils.mergeDoc(files,mergeDocFile);

        System.out.println("合并word成功");

        return true;
    }

;