基于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;
}