springboot合并word文档(docx4j)

因项目需要把多个word文档合并,所以上网查了很多资料,最终用docx4j实现,docx4j 是一个基于 Java 的 Java 的开源库,专门用于处理 Microsoft Word 的 DOCX 格式文件。它允许开发者在 Java 应用中创建、读取、修改和生成符合 Office Open XML(OOXML)规范的 Word 文档(.docx 格式),无需依赖 Microsoft Office 软件。

话不多说,直接开始

一、导入依赖

因为项目本身就有slf4j,所以这里就屏蔽


        <!-- https://mvnrepository.com/artifact/org.docx4j/docx4j -->
        <dependency>
            <groupId>org.docx4j</groupId>
            <artifactId>docx4j</artifactId>
            <version>6.1.2</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

二、编写代码

1、根据文件路径合并转成word文档

通过文件路径进行合并,然后通过响应流返回,或者放开代码里面的注释,使用保存文件的方式,想必各位大佬都会吧,不用我再教你们如何创建文件的Output流了吧

//        OutputStream outputStream = new FileOutputStream(outputPath);
//        targetPackage.save(outputStream);
/**
     * 合并多个 DOCX 文件(文件路径)
     * @param files 待合并的 DOCX 文件路径列表
     * @param response HTTP 响应对象(输出合并后的文件)
     */
    public static void mergeDocxFiles(List<String> files, HttpServletResponse response) throws Exception {
        if (files == null || files.size() < 1) {
            return;
        }
        // 1. 加载第一个文档作为目标文档(样式基准)
        WordprocessingMLPackage targetPackage = WordprocessingMLPackage.load(new File(files.get(0)));
        MainDocumentPart targetMainPart = targetPackage.getMainDocumentPart();

        // 2. 遍历剩余文档,先合并样式,再复制内容
        for (int i = 1; i < files.size(); i++) {
            File sourceFile = new File(files.get(i));
            if (!sourceFile.exists()) {
                throw new IllegalArgumentException("文件不存在:" + sourceFile.getAbsolutePath());
            }

            try (InputStream sourceStream = new FileInputStream(sourceFile)) {
                // 加载源文档
                WordprocessingMLPackage sourcePackage = WordprocessingMLPackage.load(sourceStream);
                MainDocumentPart sourceMainPart = sourcePackage.getMainDocumentPart();

                // 关键步骤1:合并源文档的样式到目标文档(避免样式丢失)
                mergeStyles(targetPackage, sourcePackage);

                // 关键步骤2:添加分页符分隔不同文档(可选,根据需求调整)
//                addPageBreak(targetMainPart);

                // 关键步骤3:逐段复制源文档内容(替代 AltChunk,解决格式混乱)
                copyContent(sourceMainPart, targetMainPart);
            }
        }
// 使用文件流的方式
//        OutputStream outputStream = new FileOutputStream(outputPath);
//        targetPackage.save(outputStream);
        targetPackage.save(response.getOutputStream());
    }

    /**
     * 合并源文档的样式到目标文档(避免样式冲突)
     */
    private static void mergeStyles(WordprocessingMLPackage target, WordprocessingMLPackage source) throws Exception {
        // 获取目标文档和源文档的样式部分
        Styles targetStyles = target.getMainDocumentPart().getStyleDefinitionsPart().getJaxbElement();
        Styles sourceStyles = source.getMainDocumentPart().getStyleDefinitionsPart().getJaxbElement();

        // 复制源文档中目标文档没有的样式
        for (Style sourceStyle : sourceStyles.getStyle()) {
            boolean styleExists = targetStyles.getStyle().stream()
                    .anyMatch(targetStyle -> targetStyle.getStyleId().equals(sourceStyle.getStyleId()));
            if (!styleExists) {
                targetStyles.getStyle().add(sourceStyle); // 添加新样式
            }
        }
    }

    /**
     * 复制源文档的内容(段落、表格等)到目标文档
     */
    private static void copyContent(MainDocumentPart sourcePart, MainDocumentPart targetPart) {
        // 获取源文档的所有内容
        List<Object> sourceContent = sourcePart.getJaxbElement().getBody().getContent();

        // 获取目标文档的主体
        Body targetBody = targetPart.getJaxbElement().getBody();

        for (Object content : sourceContent) {
            // 不过滤空段落,确保所有内容都被复制
            targetBody.getContent().add(content);
        }
    }

2、根据文件流集合转成word文档


    /**
     * 合并多个 DOCX 文件(流)
     * @param sourceStreams
     * @param response
     * @throws Exception
     */
    public static void mergeDocxFilesStreams(List<InputStream> sourceStreams, HttpServletResponse response) throws Exception {
        if (sourceStreams == null || sourceStreams.size() < 1) {
            return;
        }
        // 1. 加载第一个文档作为目标文档(样式基准)
        WordprocessingMLPackage targetPackage = WordprocessingMLPackage.load(sourceStreams.get(0));
        MainDocumentPart targetMainPart = targetPackage.getMainDocumentPart();

        try {
            for (InputStream sourceStream : sourceStreams) {
                // 加载源文档
                WordprocessingMLPackage sourcePackage = WordprocessingMLPackage.load(sourceStream);
                MainDocumentPart sourceMainPart = sourcePackage.getMainDocumentPart();
                // 关键步骤1:合并源文档的样式到目标文档(避免样式丢失)
                mergeStyles(targetPackage, sourcePackage);
                // 关键步骤2:添加分页符分隔不同文档(可选,根据需求调整)
//                addPageBreak(targetMainPart);

                // 关键步骤3:逐段复制源文档内容(替代 AltChunk,解决格式混乱)
                copyContent(sourceMainPart, targetMainPart);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        targetPackage.save(response.getOutputStream());
    }

3、把xml文档转成word文档

springboot 利用freemarker模板生成word文档_freemarker html片段转docx-优快云博客

利用freemarker模板生成word文档,是xml格式,手机是不能直接打开的,需要使用工具才能打开,这很不方便,这个也是我们客户发现的,哈哈哈哈哈,所以我就突然想到是不是可以使用这篇文章的方式实现文件转换,没想到还真的可以,下面是示例代码。

public class WordUtil {
    /**
     * 生成 word 文件
     *
     * @param dataMap 待填充数据
     * @param templateName 模板文件名称
     * @param filePath 模板文件路径
     * @param fileName 生成的 word 文件名称
     * @param response 响应流
     */
    public static void createWord(Map dataMap, String templateName, String filePath, String fileName, HttpServletResponse response){
        // 创建配置实例
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
        // 设置编码
        configuration.setDefaultEncoding(StandardCharsets.UTF_8.name());
        // ftl模板文件
        configuration.setClassForTemplateLoading(WordUtil.class, filePath);
        try {
            // 创建临时文件
            File tempFile = File.createTempFile("temp", ".doc");
            Template template = configuration.getTemplate(templateName);
            // 保存为临时文件
            FileOutputStream outputStream = new FileOutputStream(tempFile);
            Writer out = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8));
            template.process(dataMap, out);
            // 关键修改:输出为docx格式
            response.setHeader("Content-disposition",
                    "inline;filename=" + URLEncoder.encode(fileName + ".doc", StandardCharsets.UTF_8.name()));
            // 修改MIME类型为docx
            response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
            // 把响应流转成input流
            DocxMerger.mergeDocxFilesStream(new FileInputStream(tempFile), response);
            out.flush();
            out.close();
            tempFile.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


    /**
     * 把流中的DOCX文件转成doc文件
     * @param sourceStream
     * @param response
     * @throws Exception
     */
    public static void mergeDocxFilesStream(InputStream sourceStream, HttpServletResponse response) throws Exception {
        // 1. 加载第一个文档作为目标文档(样式基准)
        WordprocessingMLPackage targetPackage = WordprocessingMLPackage.load(sourceStream);
        MainDocumentPart targetMainPart = targetPackage.getMainDocumentPart();

        try {
            // 加载源文档
            WordprocessingMLPackage sourcePackage = WordprocessingMLPackage.load(sourceStream);
            MainDocumentPart sourceMainPart = sourcePackage.getMainDocumentPart();
            // 关键步骤1:合并源文档的样式到目标文档(避免样式丢失)
            mergeStyles(targetPackage, sourcePackage);
            // 关键步骤2:添加分页符分隔不同文档(可选,根据需求调整)
//                addPageBreak(targetMainPart);

            // 关键步骤3:逐段复制源文档内容(替代 AltChunk,解决格式混乱)
            copyContent(sourceMainPart, targetMainPart);
        }catch (Exception e){
            e.printStackTrace();
        }
        targetPackage.save(response.getOutputStream());
    }

总结

所有事物都具有通用性和适应性,工具或功能的设计往往不局限于单一场景。通过简单的调整或重新配置,它们可以匹配不同的应用环境或需求,其价值在于灵活运用。稍加修改或重新组合,就能适应新的场景或解决不同的问题。这种灵活性是高效解决问题的关键。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值