java合并多个DOCX文件为主要推荐方法 word图片

package com.baise.web.backend.utils;

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;

import java.io.*;
import java.util.*;

public class DocxMergerUtil {

/**
 * 合并多个DOCX文件为主要推荐方法 word图片
 */
public static void mergeDocxFiles(List<String> inputFiles, String outputFile)  {
    try {
        // 确保输出目录存在
        File output = new File(outputFile);
        output.getParentFile().mkdirs();

        if (inputFiles == null || inputFiles.isEmpty()) {
            throw new IllegalArgumentException("输入文件列表不能为空");
        }

        try (FileOutputStream dest = new FileOutputStream(output)) {
            List<XWPFDocument> documentList = new ArrayList<>();

            // 加载所有文档并保持它们打开状态直到处理完成
            List<OPCPackage> packageList = new ArrayList<>();
            try {
                for (String filePath : inputFiles) {
                    FileInputStream in = new FileInputStream(filePath);
                    OPCPackage open = OPCPackage.open(in);
                    packageList.add(open);
                    XWPFDocument document = new XWPFDocument(open);
                    documentList.add(document);
                }

                // 以第一个文档为基础进行合并
                XWPFDocument baseDoc = documentList.get(0);

                // 合并其余文档
                for (int i = 1; i < documentList.size(); i++) {
                    // 在文档之间添加分页符
                    documentList.get(i).createParagraph().setPageBreak(true);
                    try {
                        appendDocument(baseDoc, documentList.get(i));
                    } catch (Exception e) {
                        System.out.println("文件合并error !" + e);
                    }
                }

                // 写入最终文档
                baseDoc.write(dest);
                System.out.println("文件合并完成!");
            } finally {
                // 确保所有包都被正确关闭
                for (OPCPackage pkg : packageList) {
                    try {
                        pkg.close();
                    } catch (Exception e) {
                        // 忽略关闭异常
                    }
                }
            }
        }
    } catch (Exception e) {
        System.out.println("文件合并error !" + e);
    }
}

/**
 * 将源文档内容追加到目标文档
 * 参考WordMerger的实现方式优化
 */
private static void appendDocument(XWPFDocument target, XWPFDocument source) throws Exception {
    CTBody targetBody = target.getDocument().getBody();
    CTBody sourceBody = source.getDocument().getBody();

    // 复制图片并建立映射关系
    Map<String, String> pictureRelationMap = new HashMap<>();
    List<XWPFPictureData> allPictures = source.getAllPictures();

    // 预先读取所有图片数据以避免延迟加载问题
    Map<String, byte[]> pictureDataMap = new HashMap<>();
    Map<String, Integer> pictureTypeMap = new HashMap<>();

    for (XWPFPictureData picture : allPictures) {
        String relationId = source.getRelationId(picture);
        // 立即读取图片数据,避免延迟加载问题
        byte[] data = picture.getData();
        pictureDataMap.put(relationId, data);
        pictureTypeMap.put(relationId, picture.getPictureType());
    }

    // 添加图片到目标文档
    for (Map.Entry<String, byte[]> entry : pictureDataMap.entrySet()) {
        String oldRelationId = entry.getKey();
        byte[] data = entry.getValue();
        int pictureType = pictureTypeMap.get(oldRelationId);
        String newRelationId = target.addPictureData(data, pictureType);
        pictureRelationMap.put(oldRelationId, newRelationId);
    }

    // 合并文档内容
    appendBody(targetBody, sourceBody, pictureRelationMap);
}

/**
 * 合并两个文档的body部分,并更新图片引用
 * 基于WordMerger的实现方式
 */
private static void appendBody(CTBody target, CTBody source, Map<String, String> pictureRelationMap) throws Exception {
    try {
        XmlOptions optionsOuter = new XmlOptions();
        optionsOuter.setSaveOuter();

        String sourceXml = source.xmlText(optionsOuter);
        String targetXml = target.xmlText();

        // 提取XML各部分
        String prefix = targetXml.substring(0, targetXml.indexOf(">") + 1);
        String mainPart = targetXml.substring(targetXml.indexOf(">") + 1, targetXml.lastIndexOf("<"));
        String suffix = targetXml.substring(targetXml.lastIndexOf("<"));
        String addPart = sourceXml.substring(sourceXml.indexOf(">") + 1, sourceXml.lastIndexOf("<"));

        // 更新图片引用ID
        if (pictureRelationMap != null && !pictureRelationMap.isEmpty()) {
            for (Map.Entry<String, String> entry : pictureRelationMap.entrySet()) {
                addPart = addPart.replace(entry.getKey(), entry.getValue());
            }
        }

        // 解析合并后的XML并设置到目标文档
        CTBody mergedBody = CTBody.Factory.parse(prefix + mainPart + addPart + suffix);
        target.set(mergedBody);
    } catch (XmlException e) {
        System.out.println("XML解析错误:" + e);
    }
}

public static void main(String[] args) {
    try {
        List<String> files = Arrays.asList(
                "/Users/docs/11.docx",
                "/Users/docs/22.docx"
        );

        String output = "/Users/docs/1122.docx";

        // 使用优化后的方法
        mergeDocxFiles(files, output);

        System.out.println("文件合并完成!");
    } catch (Exception e) {
        System.err.println("合并文件时出错: " + e.getMessage());
        e.printStackTrace();
    }
}

}

posted @ 2025-07-25 11:43  年少轻狂请多指教  阅读(55)  评论(0)    收藏  举报